Dlaczego optymalizator wybrałby Indeks klastrowany + Sortuj zamiast Indeks nieklastrowany?

11

Biorąc pod uwagę następny przykład:

IF OBJECT_ID('dbo.my_table') IS NOT NULL
    DROP TABLE [dbo].[my_table];
GO

CREATE TABLE [dbo].[my_table]
(
    [id]    int IDENTITY (1,1)  NOT NULL PRIMARY KEY,
    [foo]   int                 NULL,
    [bar]   int                 NULL,
    [nki]   int                 NOT NULL
);
GO

/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
    ABS(CHECKSUM(NewId())) % 14,
    ABS(CHECKSUM(NewId())) % 20,
    n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM 
    sys.all_objects AS s1 
CROSS JOIN 
    sys.all_objects AS s2
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC);
GO

Jeśli pobiorę wszystkie rekordy uporządkowane według [nki](Indeks nieklastrowany):

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms

Optymalizator wybiera indeks klastrowany, a następnie stosuje algorytm sortowania.

wprowadź opis zdjęcia tutaj

Execution plan

Ale jeśli wymuszę użycie indeksu nieklastrowego:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms

Następnie używa indeksu nieklastrowego z wyszukiwaniem klucza:

wprowadź opis zdjęcia tutaj

Execution plan

Oczywiście, jeśli indeks nieklastrowany zostanie przekształcony w indeks pokrywający:

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC)
    INCLUDE (id, foo, bar);
GO

Następnie używa tylko tego indeksu:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms

wprowadź opis zdjęcia tutaj

Execution plan


Pytanie

  • Dlaczego program SQL Server używa indeksu klastrowego i algorytmu sortowania zamiast indeksu nieklastrowego, nawet jeśli w drugim przypadku czas wykonania jest o 38% krótszy?
McNets
źródło
1
Czy chodziło Ci o pominięcie ORDER BY w zapytaniu dotyczącym indeksu wymuszonego?
Forrest

Odpowiedzi:

9

Dlaczego program SQL Server używa indeksu klastrowego i algorytmu sortowania zamiast indeksu nieklastrowego, nawet jeśli w drugim przypadku czas wykonania jest o 38% krótszy?

Ponieważ SQL Server używa optymalizatora opartego na kosztach opartego na statystykach, a nie informacjach o środowisku wykonawczym.

Podczas procesu szacowania kosztów dla tego zapytania faktycznie ocenia plan wyszukiwania, ale szacuje, że zajmie to więcej wysiłku. (Zwróć uwagę na „Szacowany koszt poddrzewa”, gdy najedziesz kursorem myszy na SELECT w planie wykonania). To niekoniecznie złe założenie - na moim komputerze testowym plan wyszukiwania zajmuje 6X procesora sortowania / skanowania.

Spójrz na odpowiedź Roba Farleya, dlaczego SQL Server może kosztować plan wyszukiwania wyższy.

Forrest
źródło
9

Jeśli porównasz liczbę odczytów wymaganą w 100 000 odnośników z tym, co wiąże się z wykonaniem sortowania, możesz szybko zorientować się, dlaczego Optymalizator zapytań stwierdza, że ​​CIX + Sort byłby najlepszym wyborem.

Wykonywanie wyszukiwania kończy się szybciej, ponieważ czytane strony są w pamięci (nawet jeśli wyczyścisz pamięć podręczną, masz wiele wierszy na stronę, więc czytasz te same strony w kółko, ale z różną fragmentacją lub inna presja pamięci od innej aktywności, może nie być tak). Naprawdę nie wymagałoby to aż tyle, aby CIX + Sort działał szybciej, ale widzisz, ponieważ koszt czytania nie bierze pod uwagę względnej taniej wielokrotnego odwiedzania tych samych stron.

Rob Farley
źródło
4

Postanowiłem trochę zagłębić się w to pytanie i znalazłem kilka interesujących dokumentów mówiących o tym, jak i kiedy używać, a może lepiej, a nie (wymuszać) stosowanie indeksu nieklastrowanego.

Jak sugerują komentarze Johna Eisbrenera , jednym z najczęściej cytowanych, nawet na innych blogach, jest ten interesujący artykuł Kimberly L. Tripp:

ale nie jest to jedyny, jeśli jesteś zainteresowany, możesz spojrzeć na te strony:

Jak widać, wszystkie poruszają się wokół koncepcji punktu krytycznego .

Cytat z artykułu KL Tripp

Jaki jest punkt krytyczny?

Jest to punkt, w którym liczba zwracanych wierszy „ nie jest już wystarczająco selektywna ”. SQL Server decyduje się NIE używać indeksu nieklastrowanego do wyszukiwania odpowiednich wierszy danych i zamiast tego wykonuje skanowanie tabeli.

Gdy SQL Server używa indeksu nieklastrowego na stercie, w zasadzie pobiera listę wskaźników do stron tabeli podstawowej. Następnie używa tych wskaźników, aby pobrać wiersze za pomocą serii operacji o nazwie Wyszukiwanie identyfikatorów wierszy (RID). Oznacza to, że przynajmniej użyje tyle odczytów strony, ile zwróconych wierszy, i być może więcej. Proces jest nieco podobny z indeksem klastrowym jak tabela podstawowa, z tym samym rezultatem: więcej odczytów.

Ale kiedy nastąpi ten punkt krytyczny?

Oczywiście, jak większość rzeczy w tym życiu, zależy ...

Nie poważnie, występuje między 25% a 33% liczby stron w tabeli, w zależności od liczby wierszy na stronie. Ale jest więcej czynników, które należy wziąć pod uwagę:

Cytat z artykułu ITPRoToday

Inne czynniki wpływające na punkt krytyczny Chociaż koszt wyszukiwania RID jest najważniejszym czynnikiem wpływającym na punkt krytyczny, istnieje wiele innych czynników:

  • Fizyczne operacje we / wy są znacznie wydajniejsze podczas skanowania indeksu klastrowego. Dane indeksu klastrowego są umieszczane sekwencyjnie na dysku w kolejności indeksu. W rezultacie na dysku jest bardzo mało bocznego ruchu głowicy, co poprawia wydajność we / wy.
  • Gdy silnik bazy danych skanuje indeks klastrowany, wie, że istnieje duże prawdopodobieństwo, że kilka kolejnych stron ścieżki dysku będzie nadal zawierało dane, których potrzebuje. Więc zaczyna czytać z wyprzedzeniem w kawałkach o wielkości 64 KB zamiast zwykłych stron o wielkości 8 KB. Powoduje to również szybsze operacje we / wy.

Teraz, jeśli ponownie wykonam moje zapytania za pomocą statystyk IO:

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WHERE nki < 20000 ORDER BY nki ;
SET STATISTICS IO OFF;

Logical reads: 312

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS IO OFF;

Logical reads: 41293

Drugie zapytanie wymaga więcej logicznych odczytów niż pierwsze.

Czy powinienem unikać indeksu nieklastrowanego?

Nie, indeks klastrowy może być przydatny, ale warto poświęcić trochę czasu i podjąć dodatkowy wysiłek, analizując, co próbujesz osiągnąć za jego pomocą.

Cytat z artykułu KL Tripp

Co powinieneś zrobić? To zależy. Jeśli dobrze znasz swoje dane i przeprowadzasz obszerne testy, możesz rozważyć skorzystanie z podpowiedzi (jest kilka sprytnych rzeczy, które możesz zrobić programowo w SP, postaram się poświęcić temu post wkrótce). Jednak znacznie lepszym wyborem (jeśli w ogóle jest to możliwe) jest rozważenie pokrycia (to naprawdę moja główna uwaga :). W moich zapytaniach zasłanianie jest nierealistyczne, ponieważ moje zapytania wymagają wszystkich kolumn (zły WYBIERZ *), ale jeśli twoje zapytania są węższe ORAZ mają wysoki priorytet, lepiej jest użyć indeksu pokrycia (w wielu przypadkach) zamiast podpowiedzi, ponieważ indeks, który obejmuje zapytanie, nigdy nie daje wskazówek.

To na razie odpowiedź na zagadkę, ale zdecydowanie jest o wiele więcej do zrobienia. Punkt krytyczny może być bardzo dobrą rzeczą - i zwykle działa dobrze. Ale jeśli okaże się, że możesz wymusić indeks i uzyskać lepszą wydajność, możesz przeprowadzić dochodzenie i sprawdzić, czy to jest to. Zastanów się, jak prawdopodobne jest podpowiedź, a teraz wiesz, gdzie możesz się skupić.

McNets
źródło