Dlaczego to zapytanie nie korzysta z mojego nieklastrowanego indeksu i jak mogę to zrobić?


12

Kontynuując pytanie dotyczące zwiększenia wydajności zapytań, chciałbym wiedzieć, czy istnieje sposób domyślnego użycia mojego indeksu.

To zapytanie jest uruchamiane za około 2,5 sekundy:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Ten działa w około 33ms:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 
ORDER BY [DateEntered], [DeviceID];

W polu [ID] (pk) znajduje się indeks klastrowany, a w [DateEntered], [DeviceID] istnieje indeks nieklastrowany. Pierwsze zapytanie wykorzystuje indeks klastrowany, drugie zapytanie korzysta z mojego indeksu nieklastrowanego. Moje pytanie składa się z dwóch części:

  • Dlaczego, skoro oba zapytania mają klauzulę WHERE w polu [DateEntered], to czy serwer używa indeksu klastrowego w pierwszym, ale nie drugim?
  • Jak sprawić, aby indeks nieklastrowany był domyślnie używany w tym zapytaniu, nawet bez sortowania? (Lub dlaczego nie chciałbym tego zachowania?)

DateEntered to DateTime, w tym przypadku używam części date, ale czasami pytam jednocześnie o datę i godzinę.
Nate

Odpowiedzi:


9

pierwsze zapytanie skanuje tabelę w oparciu o próg, który wcześniej wyjaśniłem w: Czy można zwiększyć wydajność zapytania w wąskiej tabeli z milionami wierszy?

(najprawdopodobniej zapytanie bez TOP 1000klauzuli zwróci więcej niż 46 tys. wierszy. lub niektóre gdzie od 35 tys. do 46 tys. (szary obszar ;-))

drugie zapytanie należy zamówić. Ponieważ indeks NC jest uporządkowany w żądanej kolejności, optymalizator może skorzystać z tego indeksu, a następnie przeglądać zakładki do indeksu klastrowanego, aby uzyskać brakujące kolumny w porównaniu do skanowania indeksu klastrowego, a następnie wymagać zamówić to.

odwróć kolejność kolumn w ORDER BYklauzuli i wrócisz do skanowania indeksu klastrowego, ponieważ INDEKS NC jest wtedy bezużyteczny.

edytuj zapomniałeś odpowiedzi na drugie pytanie, dlaczego tego nie chcesz

Użycie nieklastrowego niekryjącego indeksu oznacza, że ​​identyfikator RowID jest sprawdzany w indeksie NC, a następnie brakujące kolumny muszą być wyszukiwane w indeksie klastrowym (indeks klastrowany zawiera wszystkie kolumny tabeli). We / wy do wyszukiwania brakujących kolumn w indeksie klastrowym są losowe we / wy.

Kluczem tutaj jest RANDOM. ponieważ dla każdego wiersza znalezionego w indeksie NC metody dostępu muszą szukać nowej strony w indeksie klastrowym. Jest to losowe, a zatem bardzo drogie.

Z drugiej strony, optymalizator może również przejść do skanowania indeksu klastrowego. Może używać map alokacji do wyszukiwania zakresów skanowania i po prostu rozpocząć czytanie indeksu klastrowego w dużych porcjach. Jest to sekwencyjne i znacznie tańsze. (o ile twoja tabela nie jest pofragmentowana :-)) Minusem jest to, że CAŁY indeks klastrowy musi zostać odczytany. Jest to szkodliwe dla bufora i potencjalnie dużej liczby operacji we / wy. ale wciąż sekwencyjne operacje we / wy.

W twoim przypadku optymalizator decyduje gdzieś między 35k a 46k wierszy, tańsze jest pełne skanowanie indeksu klastrowego. Tak, to źle. I w wielu przypadkach z wąskimi indeksami nieklastrowanymi z WHEREklauzulami nieselektywnymi lub dużą tabelą w tym przypadku idzie to źle. (Twój stół jest gorszy, ponieważ jest to również bardzo wąski stół.)

Teraz dodanie opcji ORDER BYpowoduje, że skanowanie pełnego indeksu klastrowego jest droższe, a następnie porządkowanie wyników. Zamiast tego optymalizator zakłada, że ​​taniej jest użyć już zamówionego indeksu NC, a następnie zapłacić losową karę IO za wyszukiwanie zakładek.

Twoje zamówienie jest więc idealnym rozwiązaniem podpowiedzi do zapytania. ALE, w pewnym momencie, gdy wyniki zapytania będą tak duże, kara za losowe IO wyszukiwania zakładek będzie tak duża, że ​​będzie wolniejsza. Zakładam, że optymalizator zmieni plany z powrotem na skanowanie indeksu klastrowego przed tym punktem, ale nigdy nie masz pewności.

W twoim przypadku, o ile twoje wstawki są uporządkowane według enterdate, jak omówiono na czacie i poprzednim pytaniu (patrz link), lepiej jest utworzyć indeks klastrowy w kolumnie enterDate.


20

Wyrażenie zapytania przy użyciu innej składni może czasem pomóc w wyrażeniu chęci użycia indeksu nieklastrowanego do optymalizatora. Poniższy formularz powinien zawierać plan, który chcesz:

SELECT
    [ID],
    [DeviceID],
    [IsPUp],
    [IsWebUp],
    [IsPingUp],
    [DateEntered]
FROM [dbo].[Heartbeats]
WHERE
    [ID] IN
(
    -- Keys
    SELECT TOP (1000)
        [ID]
    FROM [dbo].[Heartbeats]
    WHERE 
        [DateEntered] >= CONVERT(datetime, '2011-08-30', 121)
        AND [DateEntered]  < CONVERT(datetime, '2011-08-31', 121)
);

Plan zapytań

Porównaj ten plan z planem utworzonym, gdy indeks nieklastrowany jest wymuszony z podpowiedź:

SELECT TOP (1000) 
    * 
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE 
    [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Plan wymuszonego indeksu

Plany są zasadniczo takie same (wyszukiwanie klucza jest niczym innym jak wyszukiwaniem indeksu klastrowego). Obie formy planu wykonają tylko jedno wyszukiwanie indeksu klastrowanego i maksymalnie 1000 wyszukiwań indeksu klastrowego.

Ważną różnicą jest pozycja najlepszego operatora. Umieszczony między dwoma poszukiwaniami, Top zapobiega zamianie przez optymalizator dwóch operacji wyszukiwania na logicznie równoważny skan indeksu klastrowanego. Optymalizator działa poprzez zastąpienie części planu logicznego równoważnymi operacjami relacyjnymi. Góra nie jest operatorem relacyjnym, więc przepisywanie zapobiega transformacji do skanowania indeksu klastrowego. Gdyby optymalizator był w stanie zmienić pozycję operatora Top, nadal wolałby skanowanie niż wyszukiwanie + wyszukiwanie ze względu na sposób szacowania kosztów.

Kalkulacja kosztów skanów i poszukiwań

Na bardzo wysokim poziomie model kosztów optymalizatora dla skanów i prób jest dość prosty: szacuje się, że 320 losowych prób kosztuje tyle samo, co odczyt 1350 stron na skanie. Prawdopodobnie jest to mało podobne do możliwości sprzętowych jakiegokolwiek konkretnego nowoczesnego systemu I / O, ale działa całkiem dobrze jako praktyczny model.

Model wprowadza również szereg uproszczeń, z których najważniejszym jest to, że każde zapytanie rozpoczyna się bez danych lub stron indeksowych znajdujących się już w pamięci podręcznej. Implikacja jest taka, że ​​każde we / wy spowoduje fizyczne we / wy - chociaż rzadko tak jest w praktyce. Nawet przy zimnej pamięci podręcznej, pobieranie z wyprzedzeniem i wyprzedzanie odczytu oznacza, że ​​potrzebne strony są w rzeczywistości prawdopodobnie w pamięci, zanim procesor zapytań ich potrzebuje.

Inną kwestią jest to, że pierwsze żądanie wiersza, którego nie ma w pamięci, spowoduje pobranie całej strony z dysku. Kolejne żądania wierszy na tej samej stronie najprawdopodobniej nie spowodują fizycznego We / Wy. Model wyceny zawiera logikę uwzględniającą niektóre z takich efektów, ale nie jest idealny.

Wszystkie te rzeczy (i więcej) oznaczają, że optymalizator zwykle przełącza się na skanowanie wcześniej, niż powinno. Losowe operacje we / wy są „znacznie droższe” niż „sekwencyjne” operacje we / wy, jeśli wynikiem operacji fizycznej jest uzyskanie szybkiego dostępu do stron w pamięci. Nawet tam, gdzie wymagany jest fizyczny odczyt, skanowanie może w ogóle nie skutkować odczytami sekwencyjnymi z powodu fragmentacji, a wyszukiwania mogą być kolokowane w taki sposób, że wzorzec jest zasadniczo sekwencyjny. Dodajmy do tego zmieniającą się charakterystykę wydajności współczesnych systemów I / O (szczególnie półprzewodnikowych), a całość zaczyna wyglądać bardzo niepewnie.

Cele rzędu

Obecność operatora Top w planie modyfikuje podejście do kalkulacji kosztów. Optymalizator jest wystarczająco inteligentny, aby wiedzieć, że znalezienie 1000 wierszy za pomocą skanowania prawdopodobnie nie będzie wymagało skanowania całego indeksu klastrowego - może zatrzymać się, gdy tylko 1000 wierszy zostanie znalezionych. Określa „cel rzędu” wynoszący 1000 wierszy u górnego operatora i korzysta z informacji statystycznych, aby stamtąd wrócić, aby oszacować, ile wierszy oczekuje od źródła wiersza (w tym przypadku skan). O szczegółach tego obliczenia pisałem tutaj .

Obrazy w tej odpowiedzi zostały utworzone za pomocą SQL Sentry Plan Explorer .

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.