Составной индекс также хорош для запросов по первому полю?

87

Допустим, у меня есть таблица с полями Aи B. Я делаю регулярные запросы на A+ B, поэтому я создал составной индекс на (A,B). Будут Aли полностью оптимизированы запросы только по составному индексу?

Кроме того, я создал индекс A, но Postgres по-прежнему использует составной индекс только для запросов A. Если предыдущий ответ положительный, я думаю, это не имеет значения, но почему он выбирает составной индекс по умолчанию, если Aдоступен один индекс?

Лучано
источник
Я попытался создать небольшой тест для этого. В моем случае, однако, двухколонный индекс использовался только тогда, когда я отбрасывал одноколонный, вне зависимости от того, какой из них был создан первым. Интересно, что если я сначала создал индекс из двух столбцов, то в первоначальном плане использовалось сканирование кучи растровых изображений. Если я создал индекс из одного столбца, затем запустил запрос (использовал сканирование индекса) и отбросил вновь созданный индекс, план с индексом из двух столбцов переключился на сканирование индекса. Смотрите шаги на SQLFiddle
dezso
@dezso Интересно. Где затраты на каждый запрос?
Лучано
Стоимость сканирования растрового индекса: 107,98, время выполнения 43 мс. Сканирование индекса в одну колонку: стоимость 8,69, в две колонки: 43,69. Время выполнения не отличается значительно (колебание больше, чем разница между ними).
Дезсо
@Luciano Можете ли вы показать explain analyzeи текст запроса?
Крейг Рингер

Ответы:

88

Это определенно. Мы обсудили это очень подробно под этим связанным вопросом:

Пространство выделяется в виде кратного числа MAXALIGN, которое обычно составляет 8 байт в 64-разрядной ОС или (гораздо реже) 4 байта в 32-разрядной ОС. Если вы не уверены, проверьте pg_controldata. Это также зависит от типов данных индексированных столбцов (некоторые требуют заполнения выравнивания) и фактического содержимого.

Индекс, скажем, для двух integerстолбцов (по 4 байта в каждом) обычно заканчивается точно таким же большим, как индекс только для одного, где еще 4 байта теряются при заполнении выравнивания.

В таком случае у планировщика запросов действительно нет недостатка в использовании индекса (a,b)- по сравнению с индексом только (a). И, как правило, для нескольких запросов предпочтительно использовать один и тот же индекс. Вероятность того, что он (или его части) окажется в (быстром) кэше, при совместном использовании возрастает.

Если вы уже поддерживаете индекс (a,b), тогда не имеет смысла создавать другой индекс просто (a)- если только он существенно не меньше. То же самое не относится к (b,a)против (a). Перейдите по ссылке в первой строке, чтобы узнать больше.

Если исходить из противоположного направления, когда вам нужен дополнительный индекс, подобный этому (a,b), подумайте о том, чтобы сбросить существующий индекс просто (a)- если это возможно. Часто это невозможно, поскольку это индекс PK или UNIQUEограничения. Начиная с Postgres 11 вы можете просто добавить bк определению ограничения INCLUDEвместо этого предложение. Подробности в руководстве.

Или создайте новый индекс (b,a)вместо этого, чтобы покрыть запросы только bдополнительно. Только для условий равенства порядок индексных выражений в индексах btree не имеет значения. Тем не менее, при использовании условий дальности. Видеть:

Есть потенциальные недостатки включения дополнительных столбцов в индекс, даже если он использует только пробел, в противном случае теряется при заполнении выравнивания:

  • Всякий раз, когда обновляется дополнительный столбец, индекс также нуждается в обновлении, что может увеличить стоимость операций записи и создать больше размазывания индекса.
  • ГОРЯЧИЕ обновления (Heap Only Tuple) для таблицы невозможны, пока задействован какой-либо столбец индекса.

Подробнее о горячих обновлениях:

Как измерить размеры объекта:

Эрвин Брандштеттер
источник
1
Не могли бы вы расширить это, чтобы сказать, что, если у меня есть индекс в столбце A и возникает необходимость добавить составной индекс (A, B), индекс A должен быть отброшен? Если повторное использование индекса повышает эффективность кэширования, и (A, B) полностью оптимизирует A, то кажется, что дополнительный индекс для A потратит пространство и потенциально замедлит работу
jvans
1
@jvans: В целом верно - с заметными исключениями и альтернативами. Я добавил параграф для решения этой проблемы.
Эрвин Брандштеттер
2

По вашему вопросу у вас есть таблица с полями A и B. Если ваш запрос:

SELECT * FROM [YOUR TBL]
WHERE A='XXXX'

Оптимизатор выберет составной индекс, чтобы избежать извлечения произвольного доступа!

BongSey
источник
-4

Это в том случае, если вы просто используете только первый в предикате.

Будет выполнено сканирование, если вы используете первые столбцы составного ключа и неключевой столбец составного ключа.

Чтобы обмануть это, вы можете просто использовать фиктивные предикаты, такие как этот, а затем неключевой столбец:

[A, B] ваш индекс, [C] - еще один столбец

Чтобы использовать индекс, вы пишете как:

SELECT
    A,B,C,D,E
FROM 
    test
WHERE
   A=1
AND
   B=B
AND 
   C=3

... почему он выбирает составной индекс по умолчанию, если доступен один индекс A?

Он будет использовать индекс только в том случае, если есть один или два предиката [A] или [A], [B]. Он не будет использовать его в порядке [B], [A] или [A], [C]. Чтобы иметь возможность использовать индекс с дополнительным столбцом [C], необходимо принудительно использовать индекс, упорядочив предикаты как [A], [B] и [C].

Farfarak
источник
2
Что именно вы достигаете с B=B? Я думаю, что вы ничего не добились, поэтому я голосую за отсутствие каких-либо доказательств, которые не просто игнорирует оптимизатор
Джек Дуглас
2
B=Bфактически так же, как B IS NOT NULL, что кажется невостребованным для. Конечно, не нужно использовать индекс на (a,b).
Эрвин Брандштеттер,