OPCJA (RECOMPILE) jest zawsze szybsza; Czemu?

169

Napotkałem dziwną sytuację, gdy dołączałem OPTION (RECOMPILE) do mojego zapytania powoduje, że działa ono w pół sekundy, podczas gdy jego pominięcie powoduje, że zapytanie trwa znacznie ponad pięć minut.

Dzieje się tak, gdy zapytanie jest wykonywane z programu Query Analyzer lub z mojego programu C # za pośrednictwem SqlCommand.ExecuteReader(). Dzwonienie (lub nie dzwonienie) DBCC FREEPROCCACHElub DBCC dropcleanbuffersnie ma znaczenia; Wyniki zapytania są zawsze zwracane natychmiastowo OPTION (RECOMPILE)i dłużej niż pięć minut bez niego. Zapytanie jest zawsze wywoływane z tymi samymi parametrami [na potrzeby tego testu].

Używam SQL Server 2008.

Jestem dość komfortowy w pisaniu SQL, ale nigdy wcześniej nie użyłem OPTIONpolecenia w zapytaniu i nie byłem zaznajomiony z całą koncepcją pamięci podręcznych planu do czasu skanowania postów na tym forum. Z postów rozumiem, że OPTION (RECOMPILE)jest to kosztowna operacja. Najwyraźniej tworzy nową strategię wyszukiwania dla zapytania. Dlaczego więc tak się dzieje, że kolejne zapytania pomijają rozszerzenieOPTION (RECOMPILE) są tak wolne? Czy kolejne zapytania nie powinny wykorzystywać strategii wyszukiwania, która została obliczona podczas poprzedniego wywołania, które zawierało wskazówkę dotyczącą ponownej kompilacji?

Czy zapytanie wymagające rekompilacji przy każdym wywołaniu jest bardzo niezwykłe?

Przepraszam za pytanie dla początkujących, ale naprawdę nie mogę się z tym pogodzić.

AKTUALIZACJA: Poproszono mnie o wysłanie zapytania ...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

Uruchamiając test z Query Analyzer, poprzedzam następujące wiersze:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

Podczas wywoływania go z mojego programu C # parametry są przekazywane za pośrednictwem SqlCommand.Parameterswłaściwości.

Na potrzeby tej dyskusji można założyć, że parametry nigdy się nie zmieniają, więc możemy wykluczyć nieoptymalny parametr pachnący jako przyczynę.

Chad Decker
źródło
3
Jakie są parametry zapytania? Przeczytaj ten artykuł. blogs.msdn.com/b/turgays/archive/2013/09/10/… Zasadniczo SQL próbuje wygenerować plan zapytań na podstawie parametrów podczas pierwszej kompilacji proc. Może wygenerować plan, który nie jest optymalny, gdy zaczniesz przekazywać inne, prawdopodobnie bardziej realistyczne parametry
Sparky
3
Czy zapytanie jest wystarczająco zwięzłe, aby je tutaj wymienić? Myślę, że Sparky jest poprawny i prawdopodobnie jest związany z węszeniem parametrów, miałem podobny problem, który mnie zmylił, dopóki nie przeczytałem tego wspaniałego artykułu: sommarskog.se/query-plan-mysteries.html
Chris
1
Ale w tym przypadku (na potrzeby tego testu) zawsze przekazuję te same parametry. Żadne inne aplikacje nie były w stanie wkraść się i wywołać zapytania przy użyciu innych parametrów. Dzięki za artykuły. Przeanalizuje.
Chad Decker
2
Może się tak zdarzyć, ponieważ wykrywa wartości parametrów i zmiennych lub wprowadza większe uproszczenia. Przykładami większych uproszczeń byłoby zapadanie X = @X OR @X IS NULLsię X=@Xi wykonywanie wyszukiwania Zobacz tutaj lub przesuwanie predykatów dalej w dół w stosunku do widoku z funkcjami okna
Martin Smith
3
Po edycji przykład Query Analyzer używa zmiennych, a nie parametrów. wartość tych nigdy nie jest wąchana, z wyjątkiem RECOMPILE. W każdym przypadku zapisz plany wykonania i przyjrzyj się różnicom.
Martin Smith

Odpowiedzi:

157

Czasami używanie OPTION(RECOMPILE)ma sens. Z mojego doświadczenia wynika, że ​​jedyną realną opcją jest użycie dynamicznego SQL. Zanim zbadasz, czy ma to sens w Twojej sytuacji, poleciłbym odbudować statystyki. Można to zrobić, uruchamiając następujące polecenie:

EXEC sp_updatestats

A następnie odtworzenie planu wykonania. Zapewni to, że po utworzeniu planu wykonania będzie on korzystał z najnowszych informacji.

Dodanie OPTION(RECOMPILE)odbudowuje plan wykonania za każdym razem, gdy wykonywane jest zapytanie. Nigdy nie słyszałem, żeby to zostało opisane jako, creates a new lookup strategyale może używamy po prostu innych terminów do tego samego.

Kiedy tworzona jest procedura składowana (podejrzewam, że wywołujesz ad-hoc sql z .NET, ale jeśli używasz sparametryzowanego zapytania, to kończy się to wywołaniem przechowywanej procedury ) SQL Server próbuje określić najbardziej efektywny plan wykonania dla tego zapytania na podstawie danych w bazie danych i przekazanych parametrów ( wykrywanie parametrów ), a następnie buforuje ten plan. Oznacza to, że jeśli utworzysz zapytanie, w którym znajduje się 10 rekordów w bazie danych, a następnie wykonasz je, gdy będzie 100 000 000 rekordów, buforowany plan wykonania może już nie być najbardziej efektywny.

Podsumowując - nie widzę tutaj żadnego powodu, dla którego OPTION(RECOMPILE)byłaby to korzyść. Podejrzewam, że wystarczy zaktualizować statystyki i plan wykonania. Odbudowywanie statystyk może być istotną częścią pracy DBA w zależności od Twojej sytuacji. Jeśli nadal masz problemy po zaktualizowaniu swoich statystyk, sugerowałbym opublikowanie obu planów wykonania.

Odpowiadając na twoje pytanie - tak, powiedziałbym, że jest bardzo nietypowe, aby najlepszą opcją była rekompilacja planu wykonania za każdym razem, gdy wykonujesz zapytanie.

Abe Miessler
źródło
22
Tak, sp_updatestats załatwiło sprawę. Trafiłeś w sedno, gdy wspomniałeś o zapytaniu początkowo uruchamianym na tabeli z 10 rekordami, a teraz tabela ma miliony rekordów. To był dokładnie mój przypadek. Nie wspomniałem o tym w poście, ponieważ nie sądziłem, że to ma znaczenie. Fascynujące rzeczy. Dzięki jeszcze raz.
Chad Decker
3
To jedyny sposób, w jaki znalazłem pracę ze zmiennymi tabelarycznymi, ponieważ SQL zawsze uważa, że ​​jest jeden wiersz. Kiedy zawiera kilka tysięcy wierszy, staje się to problemem.
Alex Zhukovskiy
4
Jeden interesujący szczegół: aktualizacja statystyk niejawnie unieważnia wszystkie buforowane plany, które używają tych statystyk, ale tylko wtedy, gdy statystyki faktycznie uległy zmianie po akcji aktualizacji . Tak więc w przypadku mocno wypaczonych tabel tylko do odczytu wydaje się, że jawne OPTION (RECOMPILE)może być jedynym rozwiązaniem.
Groo
141

Często, gdy występuje drastyczna różnica między uruchomieniem a uruchomieniem zapytania, okazuje się, że często jest to jeden z 5 problemów.

  1. STATYSTYKA- Statystyki są nieaktualne. Baza danych przechowuje statystyki dotyczące zakresu i rozkładu typów wartości w różnych kolumnach tabel i indeksów. Pomaga to silnikowi zapytań w opracowaniu „Planu” ataku dotyczącego sposobu wykonania zapytania, na przykład typu metody, której użyje do dopasowania kluczy między tabelami przy użyciu skrótu lub przeglądania całego zestawu. Możesz wywołać Update Statistics dla całej bazy danych lub tylko dla niektórych tabel lub indeksów. Spowalnia to kwerendę z jednego uruchomienia do drugiego, ponieważ gdy statystyki są nieaktualne, prawdopodobnie plan kwerend nie jest optymalny dla nowo wstawionych lub zmienionych danych dla tego samego zapytania (wyjaśnione poniżej). Natychmiastowe aktualizowanie statystyk w bazie danych produkcyjnych może nie być właściwe, ponieważ w zależności od ilości danych do próbkowania wystąpi pewne obciążenie, spowolnienie i opóźnienia. Możesz także użyć pełnego skanowania lub próbkowania, aby zaktualizować statystyki. Jeśli spojrzysz na plan zapytań, możesz również wyświetlić statystyki dotyczące używanych indeksów, na przykład za pomocą poleceniaDBCC SHOW_STATISTICS (nazwa tabeli, nazwa indeksu) . To pokaże dystrybucję i zakresy kluczy, na których opiera się plan kwerend.

  2. PRZESYKANIE PARAMETRÓW - Plan zapytania, który jest buforowany, nie jest optymalny dla określonych przekazywanych parametrów, mimo że samo zapytanie nie uległo zmianie. Na przykład, jeśli przekażesz parametr, który pobiera tylko 10 z 1 000 000 wierszy, wówczas utworzony plan kwerend może używać Hash Join, jednak jeśli przekazany parametr użyje 750 000 z 1 000 000 wierszy, utworzony plan może być skanowanie indeksu lub skanowanie tabeli. W takiej sytuacji możesz nakazać instrukcji SQL użycie opcji OPCJA (REKOMPILACJA) lub SP, aby użyć Z REKOMPILEJ. Aby poinformować Silnik, że jest to „Plan jednorazowego użytku” i nie używać planu buforowanego, który prawdopodobnie nie ma zastosowania. Nie ma reguły, jak podjąć taką decyzję, zależy to od znajomości sposobu wykorzystania zapytania przez użytkowników.

  3. INDEKSY - Możliwe, że zapytanie nie uległo zmianie, ale zmiana w innym miejscu, taka jak usunięcie bardzo użytecznego indeksu, spowolniła zapytanie.

  4. ZMIENIONO WIERSZE - Wiersze , o które pytasz, drastycznie zmieniają się w zależności od połączenia. W takich przypadkach statystyki są zwykle aktualizowane automatycznie. Jeśli jednak tworzysz dynamiczny SQL lub wywołujesz SQL w ciasnej pętli, istnieje możliwość, że używasz przestarzałego planu zapytań opartego na niewłaściwej drastycznej liczbie wierszy lub statystyk. Ponownie w tym przypadku OPCJA (RECOMPILE) jest przydatna.

  5. LOGIKA To logika, twoje zapytanie nie jest już wydajne, było w porządku dla małej liczby wierszy, ale już nie jest skalowane. Zwykle wiąże się to z bardziej dogłębną analizą planu zapytania. Na przykład nie możesz już robić rzeczy zbiorczo, ale musisz robić fragmenty i wykonywać mniejsze zatwierdzenia, lub twój cross product był w porządku dla mniejszego zestawu, ale teraz zajmuje procesor i pamięć, ponieważ skaluje się większy, może to również dotyczyć używając DISTINCT, wywołujesz funkcję dla każdego wiersza, twoje dopasowania kluczy nie używają indeksu z powodu konwersji typu CASTING lub NULLS lub funkcji ... Zbyt wiele możliwości tutaj.

Generalnie, kiedy piszesz zapytanie, powinieneś mieć w umyśle pewien obraz tego, jak z grubsza są rozprowadzane określone dane w Twojej tabeli. Na przykład kolumna może mieć równomiernie rozłożoną liczbę różnych wartości lub może być pochylona, ​​80% czasu ma określony zestaw wartości, niezależnie od tego, czy rozkład będzie się często zmieniał w czasie, czy będzie dość statyczny. Dzięki temu lepiej zrozumiesz, jak zbudować wydajne zapytanie. Ale także podczas debugowania wydajności zapytania mają podstawę do budowania hipotezy, dlaczego jest powolna lub nieefektywna.

CodeCowboyOrg
źródło
2
Dziękuję przyjacielu. To doskonała informacja. Nie byłbym w stanie zrozumieć Twojej odpowiedzi, kiedy pierwotnie opublikowałem swoje pytanie, ale teraz ma to dla mnie sens.
Chad Decker
3
PRZEKĄSKIWANIE PARAMETRÓW jest zdecydowanie największą zmorą mojego życia. Nie wiedziałem nawet o tym poleceniu, dopóki nie udało mi się odpowiedzieć na pytanie wywiadu. Moje rozwiązanie do wykrywania parametrów zawsze polegało na zahaszowaniu wartości parametrów i dodaniu „AND {hash} = {hash}”, aby sql zawsze było różne dla różnych wartości. Hack, ale zadziałał.
Jeremy Boyd
27

Aby dodać do doskonałej listy (podanej przez @CodeCowboyOrg) sytuacji, w których OPCJA (RECOMPILE) może być bardzo pomocna,

  1. Zmienne tabeli . Gdy używasz zmiennych tabeli, nie będzie żadnych wstępnie utworzonych statystyk dla zmiennej tabeli, co często prowadzi do dużych różnic między szacowanymi i rzeczywistymi wierszami w planie zapytania. Użycie opcji OPTION (RECOMPILE) w zapytaniach ze zmiennymi tabeli umożliwia wygenerowanie planu zapytania, który ma znacznie lepsze oszacowanie liczby wierszy. Szczególnie krytycznie korzystałem ze zmiennej tabeli, która była bezużyteczna i którą zamierzałem porzucić, dopóki nie dodałem OPCJA (RECOMPILE). Czas pracy wzrósł z kilku godzin do kilku minut. Jest to prawdopodobnie niezwykłe, ale w każdym razie, jeśli używasz zmiennych tabeli i pracujesz nad optymalizacją, warto sprawdzić, czy OPCJA (RECOMPILE) ma znaczenie.
DWright
źródło
1
Mam zapytanie z 5 zmiennymi tabeli. Na moim komputerze działa ponad pół godziny. Na maszynie mojego współpracownika wykonuje się to w <1 sekundę. Maszyny mają podobny sprzęt i tę samą wersję programu SQL Server. Jeśli oboje dodamy OPCJĘ (REKOMPILACJA), zostanie ona wykonana w ciągu 2 sekund na obu maszynach. We wszystkich przypadkach test wykonawczy jest przeprowadzany w SSMS. Co może powodować tę różnicę?
Adam
1
Czy możesz porównać plan wykonania tego na swojej maszynie i na komputerze współpracowników bez opcji (rekompilacja)? To może pokazać źródło tej różnicy.
DWright
1
w przypadku tabel tymczasowych to ta sama sytuacja?
Muflix
1
@muflix: dobre pytanie. Nie sądzę, aby efekt był taki sam dla tabel tymczasowych, ponieważ mają one statystyki, a silnik powinien dokonywać automatycznych wyborów rekompilacji, tak jak w przypadku innych tabel, jak sądzę (ale nie jestem pewien). Może ktoś inny wie z większą pewnością.
DWright,
2
Statystyki w tabelach tymczasowych nie są automatycznie aktualizowane ani rekompilowane, więc programista musi to zrobić.
J. Michael Wuerth
1

Pierwszą czynnością przed dostrojeniem zapytań jest defragmentacja / odbudowa indeksów i statystyk, w przeciwnym razie tracisz czas.

Musisz sprawdzić plan wykonania, aby zobaczyć, czy jest stabilny (jest taki sam, gdy zmieniasz parametry), jeśli nie, być może będziesz musiał utworzyć indeks okładki (w tym przypadku dla każdej tabeli) (wiedząc, że system możesz utworzyć taki, który jest przydatny również w przypadku innych zapytań).

jako przykład: utwórz indeks idx01_datafeed_trans On datafeed_trans (feedid, feedDate) INCLUDE (acctNo, tradeDate)

jeśli plan jest stabilny lub możesz go ustabilizować, możesz wykonać zdanie za pomocą sp_executesql ('zdanie sql'), aby zapisać i użyć ustalonego planu wykonania.

jeśli plan jest niestabilny, musisz użyć instrukcji ad-hoc lub EXEC („zdanie sql”), aby za każdym razem ocenić i utworzyć plan wykonania. (lub procedura składowana „z rekompilacją”).

Mam nadzieję, że to pomoże.

Cristian Solervicéns
źródło
1

Nekroję to pytanie, ale jest wyjaśnienie, którego nikt nie rozważał.

STATYSTYKI - statystyki nie są dostępne lub wprowadzają w błąd

Jeśli wszystkie poniższe stwierdzenia są prawdziwe:

  1. Kolumny feedid i feedDate mogą być silnie skorelowane (np. Identyfikator pliku danych jest bardziej szczegółowy niż data pliku danych, a parametr daty jest informacją zbędną).
  2. Nie ma indeksu, w którym obie kolumny są kolumnami sekwencyjnymi.
  3. Nie ma ręcznie utworzonych statystyk obejmujących obie te kolumny.

W takim przypadku serwer sql może błędnie zakładać, że kolumny są nieskorelowane, co prowadzi do niższych niż oczekiwano oszacowań liczności przy stosowaniu zarówno ograniczeń, jak i wybierania planu wykonania o złym charakterze. Rozwiązaniem w tym przypadku byłoby utworzenie obiektu statystyki łączącego dwie kolumny, co nie jest kosztowną operacją.

MonkeyPushButton
źródło