Вот результаты запроса таблицы ко второму столбцу многоколоночного индекса .
Эффекты легко воспроизвести для всех. Просто попробуйте это дома.
Я тестировал PostgreSQL 9.0.5 в Debian, используя таблицу среднего размера из реальной базы данных с 23322 строками. Он реализует отношение n: m между таблицами adr
(адрес) и att
(атрибут), но здесь это не имеет значения. Упрощенная схема:
CREATE TABLE adratt (
adratt_id serial PRIMARY KEY
, adr_id integer NOT NULL
, att_id integer NOT NULL
, log_up timestamp(0) NOT NULL DEFAULT (now())::timestamp(0)
, CONSTRAINT adratt_uni UNIQUE (adr_id, att_id)
);
UNIQUE
Ограничение эффективно реализует уникальный индекс. Я повторил тест с простым индексом, чтобы убедиться, и получил идентичные результаты, как и ожидалось.
CREATE INDEX adratt_idx ON adratt(adr_id, att_id)
Таблица сгруппирована по adratt_uni
индексу, и перед тестом я запустил:
CLUSTER adratt;
ANALYZE adratt;
Последовательное сканирование запросов (adr_id, att_id)
выполняется настолько быстро, насколько это возможно. Индекс из нескольких столбцов будет по-прежнему использоваться для условия запроса только для второго столбца индекса.
Я запустил несколько раз запросы, чтобы заполнить кэш, и выбрал лучший из десяти, чтобы получить сопоставимые результаты.
1. Запрос с использованием обоих столбцов
SELECT *
FROM adratt
WHERE att_id = 90
AND adr_id = 10;
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
(1 row)
Выход EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..3.48 rows=1 width=20) (actual time=0.022..0.025 rows=1 loops=1)
Index Cond: ((adr_id = 10) AND (att_id = 90))
Total runtime: 0.067 ms
2. Запрос с использованием первого столбца
SELECT * FROM adratt WHERE adr_id = 10
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
126 | 10 | 10 | 2008-07-29 09:35:54
125 | 10 | 13 | 2008-07-29 09:35:54
4711 | 10 | 21 | 2008-07-29 09:35:54
29322 | 10 | 22 | 2011-06-06 15:50:38
29321 | 10 | 30 | 2011-06-06 15:47:17
124 | 10 | 62 | 2008-07-29 09:35:54
21913 | 10 | 78 | 2008-07-29 09:35:54
123 | 10 | 90 | 2008-07-29 09:35:54
28352 | 10 | 106 | 2010-11-22 12:37:50
(9 rows)
Выход EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..8.23 rows=9 width=20) (actual time=0.007..0.023 rows=9 loops=1)
Index Cond: (adr_id = 10)
Total runtime: 0.058 ms
3. Запрос с использованием второго столбца
SELECT * FROM adratt WHERE att_id = 90
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
180 | 39 | 90 | 2008-08-29 15:46:07
...
(83 rows)
Выход EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..818.51 rows=83 width=20) (actual time=0.014..0.694 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.849 ms
4. Отключить индексное сканирование и растровое сканирование
SET enable_indexscan = off;
SELECT * FROM adratt WHERE att_id = 90
Вывод EXPLAIN ANALYZE:
Bitmap Heap Scan on adratt (cost=779.94..854.74 rows=83 width=20) (actual time=0.558..0.743 rows=83 loops=1)
Recheck Cond: (att_id = 90)
-> Bitmap Index Scan on adratt_uni (cost=0.00..779.86 rows=83 width=0) (actual time=0.544..0.544 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.894 ms
SET enable_bitmapscan = off
SELECT * FROM adratt WHERE att_id = 90
Выход EXPLAIN ANALYZE
:
Seq Scan on adratt (cost=0.00..1323.10 rows=83 width=20) (actual time=0.009..2.429 rows=83 loops=1)
Filter: (att_id = 90)
Total runtime: 2.680 ms
Заключение
Как и ожидалось, многостолбцовый индекс используется для запроса только по второму столбцу.
Как и ожидалось, он менее эффективен, но запрос все еще в 3 раза быстрее, чем без индекса.
После отключения сканирования индекса планировщик запросов выбирает сканирование кучи растрового изображения, которое выполняется почти так же быстро. Только после отключения он также возвращается к последовательному сканированию.
vacuum full
и areindex
. Кроме этого , это поможет индексных сканирований на первом или обоих ведущих колонн много , но больно запросов на второй колонке. В недавно кластеризованной таблице строки с одинаковыми значениями во втором столбце разбросаны так, что необходимо будет прочитать максимум блоков.ре 1) Да и нет.
Например, для запроса, который использует оба столбца,
where (user_id1, user_id2) = (1,2)
не имеет значения, какой индекс создан.Для запроса, который имеет условие только для одного из столбцов, например,
where user_id1 = 1
это имеет значение, потому что обычно оптимизатор может использовать только «ведущие» столбцы для сравнения. Таким образомwhere user_id1 = 1
, можно использовать индекс (user_id1, user_id2), но он не сможет использовать индекс (user_id2, user_id1) для всех случаев.Поиграв с этим (после того, как Эрвин так любезно показал нам настройку, где он работает), кажется, что это сильно зависит от распределения данных во втором столбце, хотя я еще не выяснил, какая ситуация позволяет оптимизатору использовать конечные столбцы для ГДЕ условия.
Oracle 11, который также может (иногда) использовать столбцы, которые не находятся в начале определения индекса.
ре 2) Да, это создаст индекс
Цитата из руководства
ре 2а)
Primary Key (user_id1,user_id2)
будет создать индекс (user_id1, user_id2) (который вы можете узнать сами очень легко, просто создавая такой первичный ключ)Я настоятельно рекомендую вам прочитать главу об индексах в руководстве , она в основном отвечает на все вопросы выше.
Кроме того, какой индекс создать? by depesz хорошо объясняет порядок в столбцах индекса и другие темы, связанные с индексом.
источник
Объявление 1)
В PostgreSQL есть ограничения, которые описывает @a_horse_with_no_name . Вплоть до версии 8.0 многоколоночные индексы можно было использовать только для запросов к ведущему столбцу (столбцам). Это было улучшено в версии 8.1. Тока по эксплуатации для Postgres 10 (обновлено) объясняет:
Акцент мой. Я могу подтвердить это из опыта.
Также см. Тестовый пример добавил мой более поздний ответ здесь .
источник
Это в ответ на ответ Джека , комментарий не будет делать.
Там не было ни одного , покрывающие индексы в PostgreSQL до версии 9.2. Из-за модели MVCC каждый кортеж в наборе результатов должен посещаться для проверки видимости. Возможно, вы думаете об Oracle.
Разработчики PostgreSQL говорят о «сканировании только по индексу» . Фактически, эта функция была выпущена в Postgres 9.2. Прочитайте сообщение фиксации .
Депеш написал очень информативный пост в блоге .
Истинные покрывающие индексы (обновление) представлены в
INCLUDE
предложении Postgres 11.Это тоже немного не так:
Как отмечалось в комментариях к моему другому ответу, я также провел тесты с таблицей из двух целых чисел и ничего больше. Индекс содержит те же столбцы, что и таблица. Размер индекса btree составляет около 2/3 размера таблицы. Недостаточно объяснить ускорение фактора 3. Я провел больше тестов, основанных на вашей настройке, упрощенных до двух столбцов и с 100000 строк. На моей установке PostgreSQL 9.0 результаты были согласованы.
Если в таблице есть дополнительные столбцы, ускорение с индексом становится более существенным, но это, конечно, не единственный фактор .
Подводя итог, основные моменты:
Многостолбцовые индексы могут использоваться с запросами к не ведущим столбцам, но ускорение составляет только фактор 3 для выборочных критериев (небольшой процент строк в результате). Чем выше для больших кортежей, тем меньше для больших частей таблицы в наборе результатов.
Создайте дополнительный индекс для этих столбцов, если важна производительность.
Если все вовлеченные столбцы включены в индекс (охватывающий индекс) и все задействованные строки (на блок) видны для всех транзакций, вы можете получить «сканирование только индекса» в pg 9.2 или новее.
источник
Они не эквивалентны и, вообще говоря, индекс (bar, baz) не будет эффективен для запросов вида
select * from foo where baz=?
Эрвин продемонстрировал, что такие индексы действительно могут ускорить запрос, но этот эффект ограничен и не имеет того порядка, в котором вы обычно ожидаете, что индекс улучшает поиск - он основан на том факте, что «полное сканирование» индекса часто быстрее, чем «полное сканирование» индексированной таблицы из-за дополнительных столбцов в таблице, которые не отображаются в индексе.
Резюме: индексы могут помочь в запросах даже для не ведущих столбцов, но одним из двух второстепенных и относительно второстепенных способов, причем не так драматично, как обычно, вы ожидаете, что индекс будет помогать из-за его структуры btree
Обратите внимание на то, что индекс может помочь в двух случаях: если полное сканирование индекса значительно дешевле, чем полное сканирование таблицы, и либо: 1. поиск в таблице дешев (потому что их мало или они кластеризованы),
либо 2. индекс покрывает, поэтому нет поиска таблиц вообще, смотрите Erwins комментарии здесьобкатки:
запрос 1 (без индекса, с 74 буферами ):
запрос 2 (с индексом - оптимизатор игнорирует индекс - снова выбрав 74 буфера ):
запрос 2 (с индексом - и мы обманываем оптимизатор, чтобы использовать его):
Таким образом, доступ через индекс в два раза быстрее в этом случае, задействуя 30 буферов - что с точки зрения индексации «немного быстрее» !, и YMMV в зависимости от относительного размера таблицы и индекса, а также от количества отфильтрованных строк и характеристик кластеризации. данных в таблице
Напротив, запросы к ведущему столбцу используют структуру индекса btree - в этом случае используется 2 буфера :
источник