Jak ważna jest kolejność kolumn w indeksach?


173

Słyszałem, że na początku deklaracji indeksu należy umieścić kolumny, które będą najbardziej selektywne. Przykład:

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

Po pierwsze, czy to, co mówię, jest poprawne? Jeśli tak, czy prawdopodobnie zauważę duże różnice w wydajności, zmieniając kolejność kolumn w moim indeksie, czy też jest to bardziej „przyjemna do zrobienia” praktyka?

Pytam dlatego, że po wysłaniu zapytania przez DTA zaleciłem utworzenie indeksu, który miałby prawie wszystkie te same kolumny, co istniejący indeks, tylko w innej kolejności. Rozważałem po prostu dodanie brakujących kolumn do istniejącego indeksu i nazwanie go dobrze. Myśli?

Odpowiedzi:


193

Spójrz na taki indeks:

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

Zobacz, jak ograniczenie A jako pierwsze, skoro pierwsza kolumna eliminuje więcej wyników niż ograniczenie na drugiej kolumnie jako pierwsze? Jest to łatwiejsze, jeśli wyobrazisz sobie, w jaki sposób należy przejść przez indeks, kolumna 1, następnie kolumna 2, itd ... widzisz, że odcięcie większości wyników w pierwszym przejściu sprawia, że ​​drugi krok jest o wiele szybszy.

Inny przypadek, gdybyś zapytał o kolumnę 3, optymalizator nawet nie użyłby indeksu, ponieważ nie jest on w ogóle pomocny w zawężaniu zestawów wyników. Zawsze, gdy jesteś w zapytaniu, zawężenie liczby wyników do rozpatrzenia przed następnym krokiem oznacza lepszą wydajność.

Ponieważ indeks jest również przechowywany w ten sposób, nie ma możliwości cofania się w indeksie w celu znalezienia pierwszej kolumny podczas wykonywania zapytań.

Krótko mówiąc: nie, to nie na pokaz, są realne korzyści dla wydajności.


13
Na powyższym rysunku pamiętaj, że ten indeks byłby korzystny tylko wtedy, gdyby w zapytaniu określono kolumnę 1. Jeśli zapytanie określa tylko kolumnę 2 w predykacie łączenia lub wyszukiwania, nie byłoby to korzystne. Więc porządek też się tam liczy. Może to oczywiste, ale chciałem o tym wspomnieć.
CodeCowboyOrg

3
Pamiętaj również, że twój indeks jest taki jak na powyższym obrazku, a twoje zapytanie filtruje kolumnę1 i kolumnę2, ale kolumna2 jest bardziej unikalna i to, co naprawdę chcesz filtrować, to w rzeczywistości kolumna2, wtedy bardziej korzystne jest posiadanie indeksu, w którym kolumna 2 jest pierwsza. Może się to wydawać sprzeczne z intuicją, ale należy pamiętać, że indeks jest przechowywany na kilku stronach i jest drzewem z zakresem wartości, podczas gdy kolumna 1 powyżej neguje 1/2 możliwości, indeks już wie, do której strony indeksu przejść bezpośrednio dla Wartość z kolumny 2, nie jest wymagana kolumna 1 do zawężenia zestawu.
CodeCowboyOrg

4
Ten obraz nie jest dokładnym odzwierciedleniem struktury indeksów lub sposobu poruszania się po nich. Przesłałem odpowiedź korygującą ten stackoverflow.com/a/39080819/73226
Martin Smith

6
@MartinSmith Nie zgadzam się, że jest to niedokładne. Trzeba przyznać, że jest niezwykle uproszczone, co było moim zamiarem. Twoja odpowiedź, zagłębiająca się w znacznie bardziej szczegółowe informacje na temat poziomów, jest jednak doceniana przez tych, którzy chcą zagłębić się w nią. Jeśli spojrzysz na swoje drzewo, zobaczysz, co ilustruję w bardzo prosty sposób. Nie jest to bardzo unikalne ani nawet specyficzne dla SQL; Indeksowanie B-drzew jest dość powszechne w wielu przypadkach.
Nick Craver

@MartinSmith Nie zgodziłbym się również z tym, że jest to niedokładne, opisujesz standardowe zachowanie, jak osiągnąć indeks obejmujący - selektywność jest znacznie ważniejsza, gdy wykonujesz zapytania o zakres, ponieważ minimalizuje liczbę stron indeksu, które optymalizator musi skanować; może to mieć znaczenie w przypadku dużych tabel z milionami wierszy
Paul Hatcher,

127

Kolejność kolumn jest krytyczna. Teraz, która kolejność jest poprawna, zależy od tego, jak zamierzasz o nie zapytać. Indeks może służyć do dokładnego przeszukiwania lub skanowania zakresu. Dokładne przeszukiwanie ma miejsce, gdy określone są wartości dla wszystkich kolumn w indeksie, a zapytanie ląduje dokładnie w interesującym nas wierszu. W przypadku wyszukiwania kolejność kolumn nie ma znaczenia. Skanowanie zakresu ma miejsce, gdy określone są tylko niektóre kolumny, w tym przypadku, gdy kolejność staje się ważna. SQL Server może używać indeksu do skanowania zakresu tylko wtedy, gdy określono skrajną lewą kolumnę i tylko wtedy, gdy określono następną skrajną lewą kolumnę i tak dalej. Jeśli masz indeks na (A, B, C), można go użyć do przeszukiwania zakresu w poszukiwaniu A=@a, A=@a AND B=@bale nie w poszukiwaniu B=@b, w poszukiwaniu C=@cnor B=@b AND C=@c. Sprawa A=@a AND C=@cjest mieszana, jak wA=@aczęść użyje indeksu, ale C=@cnie (zapytanie przeskanuje wszystkie wartości B A=@a, nie przeskoczy do C=@c). Inne systemy bazodanowe mają tak zwany operator „skip scan”, który może w pewnym stopniu wykorzystać wewnętrzne kolumny w indeksie, gdy kolumny zewnętrzne nie są określone.

Mając tę ​​wiedzę, możesz ponownie przyjrzeć się definicjom indeksu. Indeks włączony (MostSelective, SecondMost, Least)będzie obowiązywał tylko wtedy, gdy MostSelectiveokreślono kolumnę. Ale ponieważ jest to najbardziej selektywne, znaczenie wewnętrznych kolumn szybko się zmniejszy. Bardzo często okaże się, że lepszy indeks jest włączony (MostSelective) include (SecondMost, Least)lub włączony (MostSelective, SecondMost) include (Least). Ponieważ kolumny wewnętrzne są mniej istotne, umieszczenie kolumn o niskiej selektywności we właściwych pozycjach w indeksie sprawia, że ​​są one niczym innym jak hałasem podczas wyszukiwania, dlatego sensowne jest przeniesienie ich ze stron pośrednich i pozostawienie ich tylko na stronach liści, ponieważ zapytania dotyczące pokrycia. Innymi słowy, przenieś je do INCLUDE. Staje się to ważniejsze wraz ze wzrostem rozmiaru Leastkolumny. Chodzi o to, że ten indeks może przynieść korzyści tylko zapytaniom, które określająMostSelective albo jako dokładna wartość, albo jako zakres, a ta kolumna jest najbardziej selektywna, ogranicza już w dużym stopniu wiersze kandydatów.

Z drugiej strony indeks na (Least, SecondMost, MostSelective)może wydawać się błędem, ale w rzeczywistości jest to dość silny indeks. Ponieważ ma Leastkolumnę jako najbardziej zewnętrzne zapytanie, może być używane do zapytań, które muszą agregować wyniki w kolumnach o niskiej selektywności. Takie zapytania są powszechne w OLAP i hurtowniach danych analitycznych i właśnie w tym przypadku takie indeksy mają bardzo dobre uzasadnienie. Takie indeksy w rzeczywistości stanowią doskonałe indeksy klastrowe , właśnie dlatego, że organizują fizyczny układ na dużych fragmentach powiązanych wierszy (ta sama Leastwartość, która zwykle wskazuje na jakąś kategorię lub typ) i ułatwiają zapytania analityczne.

Tak więc niestety nie ma „właściwej” kolejności. Nie powinieneś przestrzegać żadnego przepisu na obcinanie ciasteczek, ale zamiast tego przeanalizuj wzorzec zapytania, którego zamierzasz użyć w tych tabelach i zdecyduj, która kolejność kolumn indeksu jest właściwa.


3
Znakomita odpowiedź, jak zwykle Remus. Jeszcze kilka razy przeczytam twój trzeci akapit i kontynuuję. Podejrzewam, że to może być dokładnie to, czego potrzebuję.
Abe Miessler

„SQL Server może używać indeksu do skanowania zakresu tylko wtedy, gdy określono skrajną lewą kolumnę i tylko wtedy, gdy określono następną skrajną lewą kolumnę i tak dalej”. Właśnie tego brakowało w moim zrozumieniu, dzięki! Nie wiedziałem, że skanowanie zakresu można wykonać tylko w skrajnie prawej używanej kolumnie indeksu, ale teraz, gdy to robię, ma to sens.
Allon Guralnek

Czy to wyjaśnienie ma zastosowanie do Oracle DB?
kolejny

1
@Roizpi Tak, w zasadzie każda baza danych relacji z indeksami działa w ten sam lub bardzo podobny sposób.
Tatranskymedved

45

Jak mówi Remus, zależy to od obciążenia pracą.

Chcę jednak odnieść się do mylącego aspektu przyjętej odpowiedzi.

W przypadku zapytań, które wykonują wyszukiwanie według równości we wszystkich kolumnach indeksu, nie ma znaczącej różnicy.

Poniższe tworzy dwie tabele i wypełnia je identycznymi danymi. Jedyna różnica polega na tym, że jeden ma klucze w kolejności od najbardziej do najmniej selektywnej, a drugi odwrotnie.

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;

Teraz wykonuję zapytanie względem obu tabel ...

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 = '~'; 

... Obaj stosują indeksową grzywnę i obaj otrzymują dokładnie ten sam koszt.

wprowadź opis obrazu tutaj

Sztuka ASCII w przyjętej odpowiedzi nie jest w rzeczywistości strukturą indeksów. Poniżej przedstawiono strony indeksu dla tabeli 1 (kliknij obraz, aby otworzyć w pełnym rozmiarze).

wprowadź opis obrazu tutaj

Strony indeksowe zawierają wiersze zawierające cały klucz (w tym przypadku w rzeczywistości jest dodana dodatkowa kolumna klucza dla identyfikatora wiersza, ponieważ indeks nie został zadeklarowany jako unikalny, ale można go pominąć, więcej informacji na ten temat można znaleźć tutaj ).

W przypadku zapytania powyżej SQL Server nie dba o selektywność kolumn. Czyni binarne przeszukiwanie strony i odkrywa, że głównym Key (PPP...,3,~ ) jest >=(JJJ...,1,~ )i < (SSS...,3,~ )tak powinno czytać strony 1:118. Następnie wyszukuje binarnie kluczowe wpisy na tej stronie i lokalizuje stronę liścia, do której ma przejść.

Zmiana indeksu w kolejności selektywności nie wpływa ani na oczekiwaną liczbę kluczowych porównań z wyszukiwania binarnego, ani na liczbę stron, po których należy przejść, aby przeszukać indeks. W najlepszym wypadku może nieznacznie przyspieszyć samo porównanie kluczy.

Czasami jednak pierwsze zamówienie indeksu najbardziej selektywnego ma sens w przypadku innych zapytań w obciążeniu.

Np. Jeśli obciążenie zawiera zapytania z obu poniższych formularzy.

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

Powyższe indeksy nie obejmują żadnego z nich. MostSelectivejest wystarczająco selektywny, aby plan z wyszukiwaniem i wyszukiwaniem był opłacalny, ale zapytanie przeciwko Leastnie jest.

Jednak ten scenariusz (nieobejmujące przeszukiwanie indeksu w podzbiorze wiodących kolumn indeksu złożonego) jest tylko jedną możliwą klasą zapytania, w której może pomóc indeks. Jeśli nigdy nie wyszukujesz MostSelectivesamodzielnie lub w połączeniu zMostSelective, SecondMost i zawsze wyszukujesz według kombinacji wszystkich trzech kolumn, ta teoretyczna zaleta jest dla Ciebie bezużyteczna.

I odwrotnie zapytania, takie jak

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

Pomogłoby to w odwrotnej kolejności do powszechnie zalecanej - ponieważ obejmuje zapytanie, może obsługiwać wyszukiwanie i zwraca wiersze w pożądanej kolejności do uruchomienia.

Więc jest to często powtarzana rada ale co najwyżej jest to heurystyczny o potencjalne korzyści dla innych zapytań - i to nie zastąpi faktycznie patrząc na twoją pracą.


31

powinieneś umieścić kolumny, które będą najbardziej selektywne na początku deklaracji indeksu.

Poprawny. Indeksy mogą być złożone - złożone z wielu kolumn - a kolejność jest ważna ze względu na zasadę znajdującą się najbardziej po lewej stronie. Powodem jest to, że baza danych sprawdza listę od lewej do prawej i musi znaleźć odpowiednie odwołanie do kolumny pasujące do zdefiniowanej kolejności. Na przykład posiadanie indeksu w tabeli adresów z kolumnami:

  • Adres
  • Miasto
  • Stan

Każde zapytanie korzystające z addresskolumny może korzystać z indeksu, ale jeśli zapytanie ma tylko odwołania albo cityi / lub state- indeks nie może zostać użyty. Dzieje się tak, ponieważ nie ma odniesienia do skrajnej lewej kolumny. Wydajność zapytania powinna wskazywać, która z nich jest optymalna - poszczególne indeksy lub wiele elementów złożonych o różnych zamówieniach. Dobra lektura: Punkt krytyczny autorstwa Kimberley Tripp


A co by było, gdyby nie była używana tylko skrajna prawa kolumna? Więc zapytanie użyło adresu i miasta, ale NIE stanu. Czy indeks byłby wtedy używany?
Abe Miessler

@Abe: Najbardziej prawy nie zostałby użyty - musisz zachować kolejność indeksów zaczynając od lewej. Panno jeden, nie mogę tego użyć.
OMG Kucyki

4
@Abe: Jeśli zapytałeś o adres i miasto, ale NIE stan - wtedy tak, indeks zostanie użyty. Innymi słowy, baza danych może używać indeksów częściowych w celu spełnienia żądania, o ile jest w stanie rozpocząć od lewej strony indeksu i przesuwać się w prawo, używając pól, których dotyczy zapytanie. Jeśli jednak zapytałeś przy użyciu adresu i stanu, ale NIE miasta, może nadal używać indeksu, ale nie będzie tak wydajne - ponieważ teraz może używać tylko części adresu indeksu (b / c jest dalej miasto i nie jest używane w zapytaniu).
JaredC

6

Wszystkie inne odpowiedzi są błędne.

Selektywność poszczególnych kolumn w indeksie złożonym nie ma znaczenia przy kompletacji zamówienia.

Oto prosty proces myślowy: w rzeczywistości indeks to konkatenacja odpowiednich kolumn.

Dając to uzasadnienie, jedyną różnicą jest porównanie dwóch „łańcuchów”, które różnią się wcześniej i później w ciągu. To niewielka część całkowitego kosztu. Nie ma „pierwszego przebiegu / drugiego przejścia”, jak wspomniano w jednej odpowiedzi.

Więc jaką kolejność należy zastosować?

  1. Zacznij od przetestowanych kolumn =w dowolnej kolejności.
  2. Następnie przyczep na jednej kolumnie zakresu.

Na przykład kolumna bardzo niska selektywność musi być w tym przypadku pierwsza:

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

Zamiana kolejności w indeksie spowodowałaby całkowite zignorowanie deleted.

(Istnieje znacznie więcej reguł dotyczących kolejności kolumn).


Czy głosowanie jest negatywne, ponieważ się mylę? Albo dlatego, że mam mocne zdanie? Albo coś innego?
Rick James,

nie był mój głos przeciw, ale usunięty = 0 wydaje mi się, że nie jest to niska selektywność? Wyobrażam sobie, że byłaby to większość wierszy w tabeli.
Greg,

@Greg - myślę, że oznacza to „niską selektywność” - to znaczy używanie deletednie pomaga zbytnio w filtrowaniu niechcianych wierszy. Czy masz lepszy przykład? (To ten, który przyszedł mi do głowy, kiedy napisałem odpowiedź.)
Rick James,

Nieporozumienie z mojej strony.
Greg,

1
@ClickOk - Dzięki. Moja książka kucharska zawiera podstawowe informacje: mysql.rjweb.org/doc.php/index_cookbook_mysql
Rick James
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.