Насколько важен порядок столбцов в индексах?

173

Я слышал, что вы должны поместить столбцы, которые будут наиболее избирательными в начале объявления индекса. Пример:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

Прежде всего, то, что я говорю, правильно? Если это так, могу ли я увидеть большие различия в производительности, изменив порядок столбцов в моем индексе, или это более полезная практика?

Причина, по которой я спрашиваю, заключается в том, что после помещения запроса через DTA рекомендуется создать индекс, в котором почти все столбцы совпадают с существующим индексом, но в другом порядке. Я думал просто добавить недостающие столбцы в существующий индекс и назвать его хорошим. Мысли?

Абе Мисслер
источник

Ответы:

193

Посмотрите на индекс как это:

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

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

В другом случае, если вы запросите в столбце 3, оптимизатор даже не будет использовать индекс, потому что он вообще не помогает сузить наборы результатов. В любое время, когда вы выполняете запрос, сужение количества результатов, которые необходимо обработать до следующего шага, означает повышение производительности.

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

Короче говоря: нет, это не для показа, есть реальные преимущества в производительности.

Ник Крейвер
источник
13
На рисунке выше, имейте в виду, что этот индекс будет полезен только в том случае, если в запросе указан столбец 1. Если в вашем запросе указан только столбец 2 в предикате присоединения или поиска, то это не будет полезным. Так что порядок там тоже важен. Может быть, это само собой разумеется, но хотел упомянуть об этом.
CodeCowboyOrg
3
Также имейте в виду, предположим, что ваш индекс такой же, как на картинке выше, а ваши запросы фильтруют столбцы 1 и 2, но столбец 2 более уникален, а то, что вы действительно хотите отфильтровать, на самом деле столбец 2, тогда выгоднее иметь индекс, где колонка 2 первая. Это может показаться нелогичным, но имейте в виду, что индекс хранится на нескольких страницах и представляет собой дерево с диапазоном значений, в то время как приведенный выше столбец 1 сводит на нет 1/2 возможности, и индекс уже знает, на какую страницу индекса перейти сразу для Значение Column2, необязательно, чтобы столбец 1 сужал набор.
CodeCowboyOrg
4
Эта картина не является точным представлением того, как индексы структурированы или перемещаются. Отправили ответ, исправляющий этот stackoverflow.com/a/39080819/73226
Мартин Смит
6
@MartinSmith Я не согласен, что это неточно. Это очень по общему признанию , весьма упрощена, что было моим намерением. Тем не менее, ваш ответ, вникший в подробности об уровнях, будет полезен тем, кто хочет углубиться в него. Если вы посмотрите на изображение своего дерева, вы увидите, что я иллюстрирую очень просто. Это не очень уникально или даже специфично для SQL; Индексирование B-деревьев довольно часто встречается во многих вещах.
Ник Крейвер
@MartinSmith Я бы также не согласился с тем, что это неточно, то, что вы описываете, - это стандартное поведение того, как прийти к покрытию индекса - селективность гораздо важнее, когда вы выполняете запросы диапазона, поскольку это минимизирует количество страниц индекса, которые оптимизатор должен сканировать; это может быть важно в больших таблицах с миллионами строк
Пол Хэтчер,
127

Порядок столбцов является критическим. Теперь, какой порядок правильный, зависит от того, как вы собираетесь запросить его. Индекс может использоваться для точного поиска или сканирования диапазона. Точный поиск - это когда значения для всех столбцов в индексе заданы, и запрос попадает точно в строку, в которой заинтересован. Для запросов порядок столбцов не имеет значения. Сканирование диапазона - это когда указаны только некоторые столбцы, и в этом случае порядок становится важным. SQL Server может использовать индекс для сканирования диапазона только в том случае, если указан крайний левый столбец, и только в том случае, если указан следующий крайний левый столбец и т. Д. Если у вас есть индекс на (A, B, C), его можно использовать для сканирования диапазона для A=@a, для, A=@a AND B=@bно не для B=@b, C=@cни для B=@b AND C=@c. Случай A=@a AND C=@cсмешанный, как вA=@aчасть будет использовать индекс, ноC=@cнет (запрос будет сканировать все значения B A=@a, не будет «пропущен» C=@c). Другие системы баз данных имеют так называемый оператор «пропустить сканирование», который может использовать некоторые преимущества внутренних столбцов в индексе, когда внешние столбцы не указаны.

Обладая этими знаниями, вы можете снова посмотреть на определения индекса. Индекс (MostSelective, SecondMost, Least)включен будет действовать только тогда, когда MostSelectiveуказан столбец. Но, будучи наиболее избирательным, релевантность внутренних столбцов быстро ухудшится. Очень часто вы обнаружите, что лучший индекс включен (MostSelective) include (SecondMost, Least)или включен (MostSelective, SecondMost) include (Least). Поскольку внутренние столбцы менее релевантны, размещение столбцов с низкой избирательностью в таких правильных позициях в индексе делает их ничем иным, как шумом для поиска, поэтому имеет смысл убрать их с промежуточных страниц и оставить их только на листовых страницах, так как цели обеспечения совместимости запросов. Другими словами, переместите их в ВКЛЮЧИТЬ. Это становится более важным по мере увеличения размера Leastстолбца. Идея состоит в том, что этот индекс может принести пользу только тем запросам, которые указываютMostSelective либо как точное значение, либо как диапазон, и этот столбец, являясь наиболее селективным, уже в значительной степени ограничивает строки-кандидаты.

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

Так что, к сожалению, «правильного» порядка нет. Вы не должны следовать никакому рецепту резака печенья, а вместо этого проанализировать шаблон запроса, который вы собираетесь использовать для этих таблиц, и решить, какой порядок столбцов индекса правильный.

Ремус Русану
источник
3
Потрясающий ответ как обычно Ремус. Я собираюсь перечитать ваш третий абзац еще несколько раз и продолжить. Я подозреваю, что это может быть именно то, что мне нужно сделать.
Абэ Мисслер
«SQL Server может использовать индекс для сканирования диапазона только в том случае, если указан крайний левый столбец, и только в том случае, если указан следующий крайний левый столбец и т. Д.» Это именно то, чего не хватало в моем понимании, спасибо! Я не знал, что сканирование диапазона может быть выполнено только на самом правом используемом столбце индекса, но теперь, когда я это делаю, это имеет смысл.
Аллон Гуралнек
Применимо ли это объяснение к БД Oracle?
еще
1
@Roizpi Да, в принципе, любая база данных отношений с индексами работает так же или очень похожим образом.
Tatranskymedved
45

Как говорит Ремус, это зависит от вашей рабочей нагрузки.

Я хочу обратиться к вводящему в заблуждение аспекту принятого ответа все же.

Для запросов, которые выполняют поиск на равенство по всем столбцам в индексе, нет существенной разницы.

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

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

Теперь делаем запрос к обеим таблицам ...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

... Они оба используют индексный штраф, и оба имеют одинаковую стоимость.

введите описание изображения здесь

Искусство ASCII в принятом ответе на самом деле не то, как индексы структурированы. Страницы указателя для Таблицы 1 представлены ниже (щелкните изображение, чтобы открыть в полном размере).

введите описание изображения здесь

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

Для вышеприведенного запроса SQL Server не заботится о селективности столбцов. Он выполняет бинарный поиск корневой страницы и обнаруживает, что ключ (PPP...,3,~ ) есть, >=(JJJ...,1,~ )и < (SSS...,3,~ )поэтому он должен прочитать страницу 1:118. Затем он выполняет двоичный поиск ключевых записей на этой странице и находит страницу листа для перехода вниз.

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

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

Например, если рабочая нагрузка содержит запросы обеих следующих форм.

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

Приведенные выше индексы не охватывают ни один из них. MostSelectiveдостаточно избирателен, чтобы составить план с поиском и поиском, но запрос против Least- нет.

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

Наоборот запросы, такие как

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

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

Так что это часто повторяемый совет, но в большинстве случаев это эвристика о потенциальной выгоде для других запросов, и она не заменяет фактического просмотра вашей рабочей нагрузки.

Мартин Смит
источник
31

Вы должны поместить столбцы, которые будут наиболее избирательными в начале объявления индекса.

Верный. Индексы могут быть составными, состоящими из нескольких столбцов, и порядок важен из-за самого левого принципа. Причина в том, что база данных проверяет список слева направо и должна найти соответствующую ссылку на столбец, соответствующую определенному порядку. Например, наличие индекса для таблицы адресов со столбцами:

  • Адрес
  • город
  • государство

Любой запрос, использующий addressстолбец, может использовать индекс, но если запрос имеет только cityи / или stateссылки - индекс не может быть использован. Это связано с тем, что на самый левый столбец нет ссылок. Производительность запросов должна указывать, какой из них оптимален - отдельные индексы или несколько композитов с разными порядками. Хорошее чтение: переломный момент , Кимберли Трипп

OMG пони
источник
Что если это был только самый правый столбец, который не использовался? Таким образом, запрос использовал адрес и город, но НЕ штат. Будет ли индекс использоваться тогда?
Абэ Мисслер
@Abe: Самая правая не будет использоваться - вы должны удовлетворять порядку индекса, начиная слева. Мисс один, не могу его использовать.
OMG Ponies
4
@Abe: Если вы запросили адрес и город, но НЕ указали - тогда да, индекс будет использоваться. Другими словами, база данных может использовать частичные индексы для удовлетворения запроса, если она может начинаться слева от индекса и перемещаться вправо, используя поля, которые запрашиваются. Однако, если вы запросили, используя Адрес и Штат, но НЕ город, он все еще может использовать индекс, но он не будет столь же эффективным - потому что теперь он может использовать только адресную часть индекса (следующий б / с город, и он не используется в запросе).
JaredC
6

Все остальные ответы неверны.

Избирательность отдельных столбцов в составном индексе не имеет значения при выборе заказа.

Вот простой мыслительный процесс: по сути, индекс - это объединение задействованных столбцов.

Если дать такое обоснование, то единственное различие заключается в сравнении двух «строк», которые отличаются раньше и позже в строке. Это крошечная часть общей стоимости. Здесь нет «первого прохода / второго прохода», как указано в одном ответе.

Итак, какой порядок следует использовать?

  1. Начните с проверенных столбцов =в любом порядке.
  2. Затем выберите один столбец диапазона.

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

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

Обмен порядка в индексе приведет к его полному игнорированию deleted.

(Есть намного больше правил для заказа столбцов.)

Рик Джеймс
источник
Является ли отрицательный голос, потому что я не прав? Или потому что у меня сильное мнение? Или что-то другое?
Рик Джеймс
не был ли мой отрицательный ответ, но удаленный = 0 для меня звучит так, как будто это не низкая селективность? Я предполагаю, что это будет большинство строк в таблице.
Грег
@ Грег - Я думаю, что это означает "низкая селективность" - то есть использование deletedне сильно помогает в фильтрации нежелательных строк. У вас есть лучший пример? (Это то, что пришло мне в голову, когда я написал ответ.)
Рик Джеймс,
Недоразумение с моей стороны.
Грег
1
@ClickOk - Спасибо. Моя кулинарная книга дает некоторую основную информацию: mysql.rjweb.org/doc.php/index_cookbook_mysql
Рик Джеймс