11.3. Составные индексы #

Индексы можно создавать и по нескольким столбцам таблицы. Например, если у вас есть таблица:

CREATE TABLE test2 (
  major int,
  minor int,
  name varchar
);

(предположим, что вы поместили в неё содержимое каталога /dev) и вы часто выполняете запросы вида:

SELECT name FROM test2 WHERE major = константа AND minor = константа;

тогда имеет смысл определить индекс, покрывающий оба столбца major и minor. Например:

CREATE INDEX test2_mm_idx ON test2 (major, minor);

В настоящее время составными могут быть только индексы типов B-дерево, GiST, GIN и BRIN. Возможность построения индекса по нескольким ключевым столбцам не зависит от возможности добавления в индекс неключевых столбцов (INCLUDE). Число столбцов в индексе ограничивается 32, включая столбцы INCLUDE. (Этот предел можно изменить при компиляции PostgreSQL; см. файл pg_config_manual.h.)

Составной индекс-B-дерево может применяться в условиях с любым подмножеством столбцов индекса, но наиболее эффективен он при ограничениях по ведущим (левым) столбцам. Точное правило состоит в том, что сканируемая область индекса всегда определяется ограничениями равенства с ведущими столбцами и ограничениями неравенства с первым столбцом, не участвующим в ограничении равенства. Ограничения столбцов правее них также проверяются по индексу, что всегда избавляет от необходимости обращаться к таблице, но это уже необязательно влияет на размер сканируемой области индекса. Если при сканировании индекса-B-дерева эффективно применяется оптимизация пропуска, ограничение для каждого столбца будет применяться при перемещении по индексу во время повторных поисков по индексу. Это может уменьшить размер области индекса, которую необходимо прочитать, даже если у одного или нескольких столбцов (расположенных перед наименее значимым столбцом индекса из предиката запроса) отсутствует обычное ограничение равенства. Пропуск при сканировании работает за счёт внутренней генерации динамического ограничения равенства, соответствующего любому возможному значению в столбце индекса (применяется только для столбцов без ограничений равенства в предикате запроса и только когда сгенерированное ограничение может использоваться совместно с последующими ограничениями столбцов из предиката запроса).

Например, если есть индекс по столбцам (x, y) и условие запроса WHERE y = 7700, при сканировании индекса-B-дерева может применяться оптимизация пропуска. Обычно это происходит, когда планировщик запросов ожидает, что повторные поиски WHERE x = N AND y = 7700 для любого возможного значения N (или для каждого значения x, которое действительно хранится в индексе) — это самый быстрый возможный подход, учитывая доступные индексы таблицы. Этот подход применяется только в случае, когда количество уникальных значений x настолько мало, что планировщик ожидает, что во время сканирования большая часть индекса будет пропущена (потому что большинство листовых страниц индекса не могут содержать соответствующие кортежи). Если уникальных значений x много, то необходимо просканировать весь индекс, поэтому в большинстве случаев планировщик предпочтёт использованию индекса последовательное сканирование таблицы.

Оптимизация пропуска при сканировании также может применяться выборочно при сканированиях B-дерева, когда есть полезные ограничения из предиката запроса. Например, если есть индекс по столбцам (a, b, c) и условие WHERE a = 5 AND b >= 42 AND c < 77, индекс будет сканироваться от первой записи a = 5 и b = 42 до последней с a = 5. Записи индекса, в которых c >= 77, никогда не будут фильтроваться на табличном уровне, но их пропуск в индексе может быть как выгодным, так и нет. В случае пропуска начинается новый поиск по индексу для перемещения от конца текущей группы a = 5 и b = N (т.е. с позиции в индексе, где появляется первый кортеж a = 5 AND b = N AND c >= 77) к началу следующей группы (т.е. позиции в индексе, где появляется первый кортеж a = 5 AND b = N + 1).

Составной индекс GiST может применяться в условиях с любым подмножеством столбцов индекса. Условия с дополнительными столбцами ограничивают записи, возвращаемые индексом, но в первую очередь сканируемая область индекса определяется ограничением первого столбца. GiST-индекс будет относительно малоэффективен, когда первый его столбец содержит только несколько различающихся значений, даже если дополнительные столбцы дают множество различных значений.

Составной индекс GIN может применяться в условиях с любым подмножеством столбцов индекса. В отличие от индексов GiST или B-деревьев, эффективность поиска по нему не меняется в зависимости от того, какие из его столбцов используются в условиях запроса.

Составной индекс BRIN может применяться в условиях запроса с любым подмножеством столбцов индекса. Подобно индексу GIN и в отличие от B-деревьев или GiST, эффективность поиска по нему не меняется в зависимости от того, какие из его столбцов используются в условиях запроса. Единственное, зачем в одной таблице могут потребоваться несколько индексов BRIN вместо одного составного индекса — это затем, чтобы применялись разные параметры хранения pages_per_range.

При этом, разумеется, каждый столбец должен использоваться с операторами, соответствующими типу индекса; ограничения с другими операторами рассматриваться не будут.

Составные индексы следует использовать обдуманно. В большинстве случаев индекс по одному столбцу будет работать достаточно хорошо и сэкономит время и место. Индексы по более чем трём столбцам вряд ли будут полезными, если только таблица не используется крайне однообразно. Описание достоинств различных конфигураций индексов можно найти в Разделе 11.5 и Разделе 11.9.