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


173

Я чув, що ви повинні розміщувати стовпці, які будуть найбільш вибіркові на початку декларації індексу. Приклад:

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

По-перше, це те, що я кажу, правильно? Якщо це так, чи я можу побачити великі відмінності в продуктивності, переставляючи порядок стовпців у моєму індексі, чи це більше «приємно робити»?

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

Відповіді:


193

Подивіться на такий індекс:

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

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

Інший випадок, якщо ви зверталися за запитом до стовпця 3, оптимізатор навіть не використовував би індекс, оскільки це зовсім не корисно для звуження наборів результатів. У будь-який момент ви запитуєте, скорочуючи кількість результатів, до яких слід звернутися до наступного кроку, означає кращу ефективність.

Оскільки індекс також зберігається таким чином, немає зворотного відстеження по індексу, щоб знайти перший стовпець, коли ви запитуєте його.

Якщо коротко: ні, це не для показу, є реальні переваги від продуктивності.


13
На малюнку вище майте на увазі, що цей індекс був би корисним, лише якщо в запиті буде вказано стовпець 1. Якщо у вашому запиті вказано лише стовпець 2 у предикаті приєднання або пошуку, це не буде корисним. Тому замовляйте і питання там. Можливо, це само собою зрозуміло, але хотілося це згадати.
CodeCowboyOrg

3
Також майте на увазі, припустимо, що ваш Індекс схожий на малюнок вище, а ваш запит фільтрується на колонку1 та стовпчик2, але колонка2 є більш унікальною, і те, що ви насправді хочете фільтрувати, - це насправді колонка2, тоді корисніше просто мати індекс, де стовпець 2 є першим. Це може здатися протиінтуїтивним, але майте на увазі, що індекс зберігається на декількох сторінках і є деревом з діапазоном значень, тоді як стовпець 1 вище заперечує 1/2 можливості, індекс вже знає, на яку сторінку індексу перейти безпосередньо Значення стовпця2, не потрібно, щоб стовпець 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значення, яке зазвичай вказує на якусь категорію чи тип) і вони полегшують запити аналізу.

Тож, на жаль, немає «правильного» порядку. Ви не повинні дотримуватися жодного рецепта вирізання файлів cookie, а натомість проаналізувати шаблон запиту, який ви збираєтеся використовувати в порівнянні з цими таблицями, і вирішити, який порядок стовпців-індексів є правильним.


3
Дивовижна відповідь, як зазвичай, Рем. Я ще раз прочитаю ваш третій абзац і продовжую. Я підозрюю, що це саме те, що мені потрібно зробити.
Abe Miessler

"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посилання - індекс не можна використовувати. Це відбувається тому, що крайній лівий стовпець не посилається. Виконання запиту має сказати вам, що оптимально - окремі індекси або кілька композицій з різними порядками. Добре читайте: Переломна точка Кімберлі Тріпп


Що робити, якщо це був лише крайній правий стовпець, який не використовувався? Отже, у запиті використовується адреса та місто, але НЕ держава. Чи використовувався би індекс тоді?
Abe Miessler

@Abe: Правий край не використовувався б - ви повинні задовольнити порядок індексу, починаючи зліва. Міс один, не можу ним скористатися.
OMG Ponies

4
@Abe: Якщо ви зверталися за адресою та містом, але НЕ вказуєте - тоді так, індекс буде використаний. Іншими словами, база даних може використовувати часткові індекси для задоволення запиту, доки вона може починати зліва від індексу та рухатися праворуч, використовуючи поля, які запитуються. Якщо ви зверталися за допомогою до адреси та штату, але НЕ місто, він все ще може використовувати індекс, але він не буде настільки ефективним - тому що тепер він може використовувати лише частину адреси індексу (b / c далі - місто, і воно не використовується в запиті).
JaredC

6

Усі інші відповіді неправильні.

Селективність окремих стовпців у складеному покажчику не має значення при виборі порядку.

Ось простий процес мислення: Ефективно, індекс - це конкатенація стовпців.

Надаючи це обґрунтування, єдина відмінність - це порівняння двох "рядків", які відрізняються раніше, ніж пізніше в рядку. Це крихітна частина загальної вартості. Немає "першого проходження / другого проходу", як згадується в одній відповіді.

Отже, який замовлення слід використовувати?

  1. Почніть із тестованих стовпців =, у будь-якому порядку.
  2. Потім натисніть на один стовпчик діапазону.

Наприклад, стовпець із дуже низькою селективністю повинен стати першим у цьому:

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

Якщо поміняти порядок в індексі, він повністю ігнорується deleted.

(Існує набагато більше правил для впорядкування стовпців.)


Чи негативне голосування, тому що я помиляюся? Або тому, що у мене є сильна думка? Або щось інше?
Рік Джеймс

не був мій нижній знак, але видалений = 0 мені здається, що це недостатня вибірковість? Я думаю, це було б більшість рядків у таблиці.
Грег

@Greg - Я думаю, що це означає "низька вибірковість" - Тобто використання deletedне дуже допомагає у фільтруванні небажаних рядків. У вас є кращий приклад? (Це те, що мені
Рік Джеймс,

Нерозуміння з мого боку.
Грег

1
@ClickOk - спасибі У моїй кулінарній книзі є основна інформація: mysql.rjweb.org/doc.php/index_cookbook_mysql
Рік Джеймс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.