Wyszukiwanie indeksu a skanowanie indeksu

64

Patrząc na plan wykonania wolno działającego zapytania, zauważyłem, że niektóre węzły przeszukują indeks, a niektóre skanują indeks.

Jaka jest różnica między wyszukiwaniem indeksu a skanowaniem indeksu?

Który działa lepiej?

W jaki sposób SQL wybiera jeden od drugiego?

Zdaję sobie sprawę, że są to 3 pytania, ale myślę, że odpowiedź na pierwsze wyjaśni inne.

Greg
źródło
6
Masz dobre referencje na temat use-the-index-luke .
Marian
7
Nie wszystkie skany są złe - czasami jest to najskuteczniejszy sposób spełnienia zapytania. Zauważ również, że nie wszystkie wyszukiwania są próbami - często są to w rzeczywistości skany zasięgu, a wyszukiwanie wskazuje tylko, jak dotarło do początku zakresu.
Aaron Bertrand
@AaronBertrand, ale jeśli dojdzie do początku zakresu i przeczyta go, oznacza to, że i tak potrzebujesz danych. Szuka także końca zakresu.
George Polevoy,

Odpowiedzi:

76

Krótka wersja: poszukiwanie jest znacznie lepsze

Mniej krótka wersja: przeszukiwanie jest na ogół znacznie lepsze, ale bardzo wiele wyszukiwań (spowodowanych na przykład złym projektem zapytań z nieprzyjemnymi skorelowanymi pod-zapytaniami lub ponieważ wykonujesz wiele zapytań w operacji kursora lub innej pętli) może być gorsze niż skanowanie, szczególnie jeśli zapytanie może zwrócić dane z większości wierszy w tabeli, której dotyczy problem.

Pomaga objąć całą rodzinę operacjami wyszukiwania danych, aby w pełni zrozumieć wpływ na wydajność.

Skany tabel: Bez żadnych indeksów, które są istotne dla twojego zapytania, planista jest zmuszony użyć skanu tabeli, co oznacza, że ​​każdy wiersz jest oglądany. Może to spowodować, że każda strona związana z danymi tabeli zostanie odczytana z dysku, co często jest najgorszym przypadkiem. Zauważ, że w przypadku niektórych zapytań użyje skanu tabeli, nawet gdy obecny jest przydatny indeks - dzieje się tak zwykle dlatego, że dane w tabeli są tak małe, że trudniej jest przeglądać indeksy (w takim przypadku można się spodziewać planuj zmiany w miarę wzrostu danych, przy założeniu, że miara selektywności indeksu jest dobra).

Skany indeksu z wyszukiwaniem wierszy: Bez indeksu, który może być bezpośrednio użyty do wyszukiwania, ale indeks zawierający odpowiednie kolumny jest obecny, można zastosować skanowanie indeksu. Na przykład, jeśli masz dużą tabelę z 20 kolumnami z indeksem na kolumnie 1, col2, col3 i wydajesz SELECT col4 FROM exampletable WHERE col2=616, w tym przypadku skanowanie indeksu w celu zapytania col2jest lepsze niż skanowanie całej tabeli. Po znalezieniu pasujących wierszy strony danych należy odczytać do pobrania col4 w celu uzyskania danych wyjściowych (lub dalszego łączenia), co jest etapem „wyszukiwania zakładek”, gdy widzisz go w planach zapytań.

Skany indeksu bez przeszukiwania wierszy: Jeśli powyższy przykład byłby,SELECT col1, col2, col3 FROM exampletable WHERE col2=616 dodatkowy wysiłek, aby odczytać strony danych, nie jest potrzebny: po col2=616znalezieniu pasujących wierszy indeksu znane są wszystkie żądane dane. Dlatego czasami widzisz kolumny, które nigdy nie będą przeszukiwane, ale prawdopodobnie zostaną poproszone o dane wyjściowe, dodane na końcu indeksów - może to zaoszczędzić wyszukiwania wierszy. Podczas dodawania kolumn do indeksu z tego i tylko tego powodu dodaj je z INCLUDEklauzulą, aby poinformować silnik, że nie musi on optymalizować układu indeksu dla zapytań opartych na tych kolumnach (może to przyspieszyć aktualizacje dokonane w tych kolumnach) . Skany indeksów mogą również wynikać z zapytań bez klauzul filtrujących: SELECT col2 FROM exampletableskanuje ten przykładowy indeks zamiast stron tabeli.

Szukanie indeksu (z wyszukiwaniem wierszy lub bez) : Podczas wyszukiwania nie uwzględnia się całego indeksu. W przypadku zapytania SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567silnik zapytań może znaleźć pierwszy wiersz, który będzie pasował, wykonując wyszukiwanie w indeksie w oparciu o drzewo, c1a następnie będzie mógł nawigować po indeksie w kolejności, aż dojdzie do końca zakresu (to samo dotyczy zapytania dla c1=1234jak może istnieć wiele wierszy spełniających warunek nawet dla =operacji). Oznacza to, że zamiast każdej strony w indeksie (lub tabeli) należy odczytać tylko odpowiednie strony indeksu (plus kilka potrzebnych do pierwszego wyszukiwania).

Indeksy klastrowe: W przypadku indeksu klastrowego dane tabeli są przechowywane w węzłach liści tego indeksu zamiast w osobnej strukturze stosu. Oznacza to, że po znalezieniu wierszy przy użyciu tego indeksu nigdy nie będą potrzebne żadne dodatkowe wyszukiwania wierszy, bez względu na to, jakie kolumny są potrzebne [chyba że masz dane poza stroną, takie jak TEXTkolumny lub VARCHAR(MAX)kolumny zawierające długie dane].

Z tego powodu możesz mieć tylko jeden indeks klastrowy [1] , indeks klastrowany jest twoją tabelą zamiast osobnej struktury sterty, więc jeśli użyjesz jednego [2], wybierz go dokładnie, aby uzyskać maksymalny zysk.

Zauważ też, że indeks klastrowany, ponieważ „klucz klastrujący” dla tabeli i jest zawarty w każdym nieklastrowanym indeksie w tabeli, więc szeroki indeks klastrowany nie jest na ogół dobrym pomysłem.

[1] W rzeczywistości można efektywnie mieć wiele indeksów klastrowych, definiując indeksy nieklastrowe, które pokrywają lub uwzględniają każdą kolumnę w tabeli, ale prawdopodobnie marnowanie miejsca ma wpływ na wydajność zapisu, więc jeśli to zrobisz, upewnij się, że naprawdę musisz.

[2] Kiedy mówię „jeśli używasz indeksu klastrowego”, należy pamiętać, że jest on ogólnie zaleca się, aby zrobić jeden na każdym stole. Istnieją wyjątki, jak w przypadku wszystkich praktycznych reguł, tabele, które widzą niewiele innych niż wstawianie zbiorcze i nieuporządkowane odczyty (być może tabele pomostowe dla procesów ETL) są najczęstszym przeciwnym przykładem.

Dodatkowy punkt: Niekompletne skany:

Ważne jest, aby pamiętać, że w zależności od reszty zapytania skanowanie tabeli / indeksu może nie skanować całej tabeli - jeśli logika na to pozwala, plan kwerend może być w stanie spowodować wcześniejsze przerwanie. Najprostszym przykładem tego jest SELECT TOP(1) * FROM HugeTable- jeśli spojrzysz na plan zapytań, zobaczysz, że ze skanu został zwrócony tylko jeden wiersz, a jeśli obejrzysz statystyki IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable), zobaczysz, że odczytuje tylko bardzo małą liczbę stron (być może tylko jedną).

To samo może się zdarzyć, jeśli predykat klauzuli WHERElub JOIN ... ONmoże być uruchomiony jednocześnie ze skanem, który jest źródłem, jeśli jego dane. Planista / moduł uruchamiający zapytania może czasami być bardzo sprytny, jeśli chodzi o przekazywanie predykatów z powrotem w kierunku źródeł danych, aby umożliwić wcześniejsze zakończenie skanowania w ten sposób (a czasami możesz być sprytny w przestawianiu zapytań, aby to zrobić!). Podczas gdy dane przepływają od prawej do lewej zgodnie ze strzałkami na standardowym ekranie planu zapytań, logika działa od lewej do prawej, a każdy krok (od prawej do lewej) niekoniecznie musi zostać zakończony, zanim można będzie rozpocząć następny. W prostym przykładzie powyżej, jeśli spojrzysz na każdy blok w planie zapytań jako agent, SELECTagent pyta TOPagenta o wiersz, który z kolei pytaTABLE SCANagent dla jednego, następnie SELECTagent prosi o inny, ale TOPagent wie, że nie ma potrzeby, nawet nie pyta czytnika tabel, SELECTagent otrzymuje odpowiedź „nie ma już znaczenia” i wie, że cała praca została wykonana. Wiele operacji blokowania tego typu optymalizacji oczywiście tak często w bardziej skomplikowanych przykładów skan tabeli / index naprawdę nie czytają każdy rząd, ale należy uważać, aby nie skakać do wniosku, że każdy skanowania musi być kosztowna operacja.

David Spillett
źródło
6

Ogólnie rzecz biorąc, próby są dobre, skany są złe.

Poszukiwane są miejsca, w których zapytanie może efektywnie wykorzystać indeks i użyć go do znalezienia potrzebnych wierszy.

Skany są miejscem, w którym zapytanie przegląda cały indeks, próbując znaleźć to, czego potrzebuje.

Jak wybiera SQL? Głęboko w wewnętrznych elementach optymalizatora zapytań decyzja jest podejmowana na podstawie zapytania i dostępnych indeksów oraz informacji statystycznych związanych z tymi indeksami.

Istnieje kilka książek do przeczytania, które mogą być tutaj interesujące - obie z księgarni Red-Gate pod adresem http://www.red-gate.com/community/books/

  • Plany wykonania programu SQL Server autorstwa Granta Fritcheya
  • Inside Query Optimizer autorstwa Benjamina Nevareza
  • Statystyka serwera SQL autorstwa Holgera Schmelinga
Thomas Rushton
źródło
7
W przypadku tego samego planu skanowanie pojedynczej tabeli jest dobre, a milion wyszukiwań jest zły. Więc twoje pierwsze stwierdzenie nie jest całkowicie poprawne.
Marian
Rzeczywiście, wyszukiwanie indeksów i skanowanie indeksów ma swoje zastosowanie, nie można powiedzieć, że jedno jest lepsze od drugiego BEZ kontekstu bazowych tabel i zapytań. W większości przypadków, jeśli statystyki są niedokładne w tabeli, plan wykonania może okazać się suboptymalny, np. Wyszukiwanie indeksu jest błędnie wybierane zamiast skanowania indeksu i odwrotnie.
jyao
5

Jeśli chcesz wykopać temat, bardzo pomocną książką (przynajmniej dla mnie) są plany wykonania SQL Server Grant Fritchey, dostępne bezpłatnie w RedGate tutaj .

Jeśli masz zapytanie takie jak

SELECT *
FROM myTable

SQL Server prawdopodobnie użyje skanowania indeksu, ponieważ musi przejść przez wszystkie wiersze, aby wyświetlić wymagane wyniki.

Przeciwnie,

SELECT *
FROM myTable
WHERE myID = 1

z pewnością spowoduje wyszukiwanie indeksu. SQL Server użyje struktury B-drzewa indeksu myID, a wyszukiwanie odpowiedniej linii będzie znacznie szybsze.

KookieMonster
źródło
Nie wiem, czy zgadzam się z „na pewno” - nawet jeśli indeks ma mój identyfikator jako wiodącą kolumnę, wyszukiwanie może nie być optymalną odpowiedzią (zależy od wielu rzeczy, takich jak to, czy jest unikalne) - które mogą być prawda w tabeli klientów, ale nie w przypadku identyfikatora klienta w tabeli zamówień, ile kolumn należy zakryć, ale nie ma ich w indeksie itp.).
Aaron Bertrand
Nie sądzę, że ta odpowiedź naprawdę obejmuje postawione pytania.
Zero3
5

Inni dość dobrze zdefiniowali różnice między wyszukiwaniem a skanowaniem. W tym przypadku samo zapytanie i planista wykonania powinny dostarczyć informacji potrzebnych do sprawdzenia, które wartości są używane jako predykaty (filtry) dla zapytania w każdej części. Zazwyczaj dobrą praktyką jest zawsze dodawanie indeksów nieklastrowanych do kluczy obcych, a zależnie od przypadków użycia w kodzie programu, warto rozważyć utworzenie dodatkowych indeksów wielokolumnowych lub uwzględnionych indeksów kolumn. Dzięki przedstawionej tutaj terminologii wyszukiwanie w Google da przyzwoite wyniki na przykładach na każdym z nich.

Ale na przykład powiedzmy, że Twój kod wysyła zapytanie o Kolumnę A i Kolumnę B na danych filtrach, ale chcesz również zwrócić wartości z Kolumny C i Kolumny E, możesz chcieć utworzyć indeks w Kolumnie A i B z INCLUDE opcja zawierająca Kolumnę C i E. W ten sposób pojedyncze wyszukiwanie indeksu zwróci wszystko, czego potrzebujesz, ponieważ nie trzeba wykonywać wyszukiwania w celu pobrania innych wartości (C i E) w tym samym wierszu.

Kahn
źródło