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.
Odpowiedzi:
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 zapytaniacol2
jest 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: pocol2=616
znalezieniu 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 zINCLUDE
klauzulą, 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 exampletable
skanuje 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 4567
silnik zapytań może znaleźć pierwszy wiersz, który będzie pasował, wykonując wyszukiwanie w indeksie w oparciu o drzewo,c1
a następnie będzie mógł nawigować po indeksie w kolejności, aż dojdzie do końca zakresu (to samo dotyczy zapytania dlac1=1234
jak 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
TEXT
kolumny lubVARCHAR(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
WHERE
lubJOIN ... ON
moż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,SELECT
agent pytaTOP
agenta o wiersz, który z kolei pytaTABLE SCAN
agent dla jednego, następnieSELECT
agent prosi o inny, aleTOP
agent wie, że nie ma potrzeby, nawet nie pyta czytnika tabel,SELECT
agent 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.źródło
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/
źródło
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
SQL Server prawdopodobnie użyje skanowania indeksu, ponieważ musi przejść przez wszystkie wiersze, aby wyświetlić wymagane wyniki.
Przeciwnie,
z pewnością spowoduje wyszukiwanie indeksu. SQL Server użyje struktury B-drzewa indeksu myID, a wyszukiwanie odpowiedniej linii będzie znacznie szybsze.
źródło
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.
źródło