Nagle powolny plan wykonania dla przechowywanego proc

15

Próbuję zrozumieć problem, który mamy z SQL Server 2000. Jesteśmy witryną o umiarkowanie transakcyjnym charakterze i mamy zapisany proces, sp_GetCurrentTransactionsktóry akceptuje ID klienta i dwie daty.

Teraz, w zależności od dat i klienta, to zapytanie może zwrócić wszystko od zera do 1000 wierszy.

Problem: doświadczyliśmy tego, że nagle dostaniemy szereg błędów (typowych Execution Timeout Expiredlub podobnych) dla konkretnego klienta podczas próby wykonania zapisanego proc. Sprawdzamy więc zapytanie, uruchamiamy je w SSMS i stwierdzamy, że zajmuje ono 30 sekund. Ponownie kompilujemy zapisany proc i -bang- działa teraz za 300ms.

Rozmawiałem o tym z naszą DBA. Powiedział mi, że baza danych utworzyła plan zapytań, kiedy utworzyliśmy przechowywany proc. Powiedział, że był to dobry plan dla tego zestawu parametrów, ale jeśli rzucisz na niego pewien zestaw parametrów, wtedy plan nie będzie najlepszym planem dla tych danych, więc zobaczysz, że działa wolno.

Opcje przedstawione mi to przeniesienie tego problemu z przechowywanego proc i z powrotem do dynamicznego SQL, który ma plan wykonania tworzony przy każdym uruchomieniu.

To wydaje mi się krokiem wstecz i wydaje mi się, że musi być jakiś sposób na obejście tego. Czy istnieje inny sposób rozwiązania tego problemu?

Wszelkie odpowiedzi są mile widziane.

Ciaran Archer
źródło
czy w procedurze znajduje się instrukcja if / else? Widziałem to, gdy plan jest buforowany w instrukcji if, a następnie próbuje wykonać w bloku else przy użyciu niewłaściwego planu. Czy te błędy odpowiadały zmianie w procedurze?
Jeremy Gray,
@Jeremy: Brak zmian w proc i brak instrukcji else / if.
Ciaran Archer,

Odpowiedzi:

14

Ten problem nazywa się wąchaniem parametrów.

Późniejsze wersje SQL Server daje więcej możliwości w kontaktach z nim, takich jak OPTION (RECOMPILE)lub OPTIMIZE FORpodpowiedzi.

Możesz spróbować zadeklarować zmienne w procedurze przechowywanej, przypisując wartości parametrów do zmiennych i używając zmiennych zamiast parametrów, ponieważ brzmi to tak, jakbyś przez większość czasu miał dość zadowalający plan.

Zwykle najbardziej katastrofalnymi złymi planami są te opracowane dla parametrów o bardzo wysokiej selektywności, ale uruchomione z parametrami o niskiej selektywności.

Zakładając, że wygenerowany plan jest bardziej niezawodny dzięki temu podejściu i zadowalający dla wszystkich wartości parametrów, wówczas przewaga tego podejścia nad sugerowanym przez JNK polega na tym, że nie pociąga on za sobą kosztów kompilacji dla każdego połączenia.

Wadą jest to, że w przypadku niektórych wykonań czas wykonywania może być dłuższy niż w przypadku planu dostosowanego specjalnie do tych wartości parametrów, więc jest to kompromis między czasem kompilacji a czasem wykonania.

Martin Smith
źródło
3
Lub „wiąż zerkanie” w terminologii Oracle
Gajusz
Dzięki @ Gaius, dobrze znana terminologia dla więcej niż jednego RDBMS;)
Andrei Rînea
6

Zamiast używać dynamicznego SQL, zawsze możesz po prostu zmienić wywołania proc na:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

Te WITH RECOMPILEsiły (zgadliście!) Ponowną kompilację planu wykonania, gdy jest on prowadzony.

Możesz również zawrzeć WITH RECOMPILEw definicji przechowywanego proc:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...
JNK
źródło
2

Możesz także spróbować zdecydować, która baza danych będzie używana, choć będziesz trochę walczył z optymalizatorem, więc będzie bardziej kruchy, niż możesz się spodziewać.

Technika jest taka - podziel procedurę przechowywaną na 2, jeden przeznaczony dla jednego zestawu parametrów, jeden dla drugiego. Dodaj do nich klauzule where, aby między nimi obejmowały wszystkie możliwe przypadki. Spójrz na plany zapytań - jeden powinien być zoptymalizowany dla jednego zestawu parametrów, drugi dla drugiego zestawu. Być może trzeba będzie majstrować przy zapytaniu, aby tak się stało, lub może nie być to możliwe w przypadku zapytania, w takim przypadku to podejście nie zadziała.

Teraz spraw, aby oryginalna procedura przechowywana sprawdziła wartości parametrów i wysłała do odpowiedniej jednej z dwóch procedur przechowywanych z poprzedniego akapitu.

Może to działać, ale jest to rodzaj włamania, aby zmusić optymalizator do bardziej efektywnej pracy dla Twojego zapytania. Podobnie jak wszystkie takie hacki, w przyszłych wersjach bazy danych może to być niepotrzebne, a nawet pogorszyć sytuację. Więc nawet jeśli to działa, musisz zdecydować, czy warto.

psr
źródło
1

Możesz także spróbować SET FORCEPLANindeksować wskazówki.

http://msdn.microsoft.com/en-us/library/ms188344.aspx
Zasadniczo pozwala wybrać kolejność łączenia.

Możesz mieć wskazówki dotyczące indeksów, aby upewnić się, że serwer SQL używa poprawnych indeksów.

użytkownik606723
źródło
0

Hmmm ... jeśli skupimy się tylko na tej jednej procedurze składowanej, byłbym zaskoczony, że użycie buforowanego planu wykonania spowoduje problem, który widzisz. Chciałbym zobaczyć plan wykonania procedury składowanej przy użyciu zestawu parametrów dla klienta i dwóch dat. Zastanawiam się, czy bardziej konkretny indeks byłby pomocny -> na przykład na customerId i tylko dwie daty?

Brian Knight
źródło
2
Skąd ta niespodzianka? wąchanie parametrów jest dość częstym problemem z tymi objawami i wygląda na to, że DBA zidentyfikowało to jako problem.
Martin Smith,
@MartinSmith - Jestem trochę zaskoczony, że DBA, która wie o węszeniu paramterów, nie wie jednak o wskazówkach ponownej kompilacji ...
JNK,
@JNK - To prawda. Nie jestem pewien, dlaczego by o tym nie wspominali.
Martin Smith,
0

Nagłe obniżenie wydajności wydaje się być nieefektywnym planem zapytań, który powstaje, prawdopodobnie w wyniku brakujących statystyk. Uruchom profilera programu SQL Server z ustawionymi kategoriami zdarzeń „Błędy i ostrzeżenia” i sprawdź, czy istnieją ostrzeżenia o brakujących statystykach.

Być może brakuje też indeksu lub może być konieczne jego defragmentowanie, ponieważ mogą być zbyt fragmentaryczne, aby można było z nich korzystać w programie SQL Server, co powoduje, że uważa się, że skanowanie tabeli spowoduje mniej operacji we / wy.

@JNK podnosi świetny punkt na temat przechowywanych procesów - są one kompilowane z góry, a plan zapytań będzie przechowywany wraz z procedurą przechowywaną.

Niekoniecznie zgadzam się na użycie Z RECOMPILE, ponieważ wtedy tracisz korzyści z zapisywania i ponownego wykorzystywania planu zapytań. W niektórych przypadkach jest to konieczne - np. Jeśli statystyki dystrybucji w tabelach leżących u podstaw różnią się znacznie między połączeniami, ale ogólnie, gdy dane w tabelach są dojrzałe, dystrybucja danych w tabelach będzie się minimalnie różnić.

Podsumowując:

  1. Sprawdź brakujące statystyki
  2. Sprawdź fragmentację indeksu
  3. Utwórz i użyj zapisanego proc
  4. Proszę zmienić nazwę proc - sp_ to miękko zarezerwowana przestrzeń nazw prefiksów dla wewnętrznych procesów SQL Server systemu - powoduje to, że SQL Server zawsze szuka najpierw w głównej bazie danych procedur przechowywanych. Zmiana nazwy proc usp_ zamiast sp_ spowoduje wzrost wydajności, ale wątpię, czy to twój problem w tym przypadku.
ifx
źródło