Mam typowy przypadek, w którym wąchanie parametrów powoduje, że „zły” plan wykonania ląduje w pamięci podręcznej planu, powodując, że kolejne wykonywanie mojej procedury składowanej jest bardzo wolne. Mogę „rozwiązać” ten problem za pomocą zmiennych lokalnych OPTIMIZE FOR ... UNKNOWN
i OPTION(RECOMPILE)
. Jednak mogę również zagłębić się w kwerendę i spróbować ją zoptymalizować.
Próbuję ustalić, czy powinienem : mając ograniczony czas na naprawę problemów, chciałbym poznać koszt nie zrobienia tego. Moim zdaniem, jeśli się tylko trzymam OPTION(RECOMPILE)
, efektem netto jest to, że plan zapytania jest odtwarzany przy każdym uruchomieniu zapytania. Myślę, że muszę wiedzieć:
Jak dowiedzieć się, jaki jest koszt utworzenia planu zapytań?
Aby odpowiedzieć na moje pytanie, przejrzałem Google (np. Z tym zapytaniem ) i przejrzałem dokumentację kolumn dla dm_exec_query_stats
DMV . Sprawdziłem także okno wyjściowe w SSMS pod kątem „Rzeczywistego planu zapytań”, aby znaleźć te informacje. Wreszcie, mam poszukiwanej DBA.SE . Żadna z nich nie doprowadziła do odpowiedzi.
Czy ktoś może mi powiedzieć? Czy jest możliwe znalezienie lub zmierzenie czasu potrzebnego na utworzenie planu?
źródło
Odpowiedzi:
Możesz spojrzeć na właściwości węzła głównego w planie zapytań, na przykład:
(zrzut ekranu z bezpłatnego Sentry One Plan Explorer )
Informacje te są również dostępne poprzez zapytanie o pamięć podręczną planu, na przykład za pomocą zapytania opartego na następujących relacjach:
Aby uzyskać pełne omówienie dostępnych opcji obsługi tego rodzaju zapytań, zobacz niedawno zaktualizowany artykuł Erland Sommarskog .
źródło
Zakładając, że „koszt” jest pod względem czasu (choć nie jestem pewien, co jeszcze może być pod względem ;-), to przynajmniej powinieneś być w stanie go zrozumieć, wykonując coś takiego:
Pierwszy element zgłaszany na karcie „Wiadomości” powinien być:
Uruchomiłbym to co najmniej 10 razy i uśredniłem zarówno „CPU”, jak i „Elapsed” milisekundy.
Najlepiej byłoby uruchomić to w dziale Production, aby uzyskać prawdziwy szacunek czasu, ale rzadko ludzie mogą wyczyścić pamięć podręczną planu w Production. Na szczęście, począwszy od SQL Server 2008 stało się możliwe usunięcie konkretnego planu z pamięci podręcznej. W takim przypadku możesz wykonać następujące czynności:
Jednak w zależności od zmienności wartości przekazywanych dla parametru (ów) powodującego „zły” buforowany plan, istnieje inna metoda, aby uznać, że jest to środek pomiędzy :
OPTION(RECOMPILE)
aOPTION(OPTIMIZE FOR UNKNOWN)
dynamicznym SQL. Tak, powiedziałem to. Mam na myśli nawet nie sparametryzowany dynamiczny SQL. Oto dlaczego.Wyraźnie masz dane, które mają nierównomierny rozkład, przynajmniej pod względem jednej lub więcej wartości parametrów wejściowych. Wady wymienionych opcji to:
OPTION(RECOMPILE)
wygeneruje plan dla każdego wykonania i nigdy nie będą mogli korzystać z dowolnego planu ponownego użycia, nawet jeśli wartości parametr przekazany ponownie są identyczne z wcześniejszym okresie (S). W przypadku proc, które są często wywoływane - raz na kilka sekund lub częściej - uratuje cię to od czasu do czasu okropnej sytuacji, ale wciąż pozostawia cię w niezbyt dobrej sytuacji.OPTION(OPTIMIZE FOR (@Param = value))
wygeneruje plan na podstawie tej konkretnej wartości, co może pomóc w kilku przypadkach, ale nadal pozostawia Cię otwartym na bieżący problem.OPTION(OPTIMIZE FOR UNKNOWN)
wygeneruje plan na podstawie średniej dystrybucji, co pomoże niektórym zapytaniom, ale zaszkodzi innym. Powinno to być takie samo, jak opcja używania zmiennych lokalnych.Jednak dynamiczny SQL, jeśli zostanie wykonany poprawnie , pozwoli, aby różne wartości były przekazywane, aby mieć własne oddzielne plany zapytań, które są idealne (cóż, o ile będą). Głównym kosztem jest to, że wraz ze wzrostem różnorodności przekazywanych wartości rośnie liczba planów wykonania w pamięci podręcznej i zajmują one pamięć. Niewielkie koszty to:
sprawdzanie poprawności parametrów ciągu, aby zapobiec wstrzyknięciom SQL
być może konieczne będzie skonfigurowanie Certyfikatu i Użytkownika opartego na Certyfikacie, aby zachować idealną abstrakcję bezpieczeństwa, ponieważ Dynamiczny SQL wymaga bezpośrednich uprawnień do tabeli.
Oto, jak poradziłem sobie z tą sytuacją, gdy miałem procy, które były wywoływane więcej niż raz na sekundę i trafiały na wiele tabel, każda z milionami wierszy. Próbowałem,
OPTION(RECOMPILE)
ale okazało się, że jest to zbyt szkodliwe dla procesu w 99% przypadków, w których nie wystąpił problem z wąchaniem parametru / błędnym planem buforowanym. I pamiętaj, że jeden z tych procesów zawierał około 15 zapytań i tylko 3 - 5 z nich zostało przekonwertowanych na Dynamic SQL, jak opisano tutaj; Dynamiczny SQL nie był używany, chyba że był konieczny dla konkretnego zapytania.Jeśli do procedury składowanej jest wiele parametrów wejściowych, dowiedz się, które z nich są używane z kolumnami, które mają bardzo zróżnicowane rozkłady danych (a zatem powodują ten problem), a które z nich są używane z kolumnami, które mają bardziej równomierne rozkłady (i nie powinny powodując ten problem).
Zbuduj ciąg dynamicznego SQL przy użyciu parametrów parametrów wejściowych proc, które są powiązane z równomiernie rozmieszczonymi kolumnami. Ta parametryzacja pomaga zmniejszyć wynikowy wzrost planów wykonania w pamięci podręcznej związanej z tym zapytaniem.
Dla pozostałych parametrów, które są powiązane z bardzo zróżnicowanymi dystrybucjami, należy je połączyć w Dynamiczny SQL jako wartości dosłowne. Ponieważ unikalne zapytanie jest określane przez wszelkie zmiany w tekście zapytania, posiadanie
WHERE StatusID = 1
jest innym zapytaniem, a zatem innym planem zapytań niż posiadanieWHERE StatusID = 2
.Jeśli którykolwiek z parametrów wejściowych proc, które mają być konkatenowane w tekście zapytania, to ciągi, należy je zweryfikować, aby zabezpieczyć przed iniekcją SQL (choć jest to mniej prawdopodobne, jeśli przekazywane ciągi są generowane przez aplikacja, a nie użytkownik, ale nadal). Przynajmniej
REPLACE(@Param, '''', '''''')
upewnij się, że pojedyncze cudzysłowy stają się znakami pojedynczymi.W razie potrzeby utwórz certyfikat, który będzie używany do utworzenia użytkownika i podpisz procedurę składowaną, aby bezpośrednie uprawnienia do tabeli były przyznawane tylko nowemu użytkownikowi opartemu na certyfikacie, a nie użytkownikom
[public]
lub użytkownikom, którzy w przeciwnym razie nie powinni mieć takich uprawnień .Przykładowy proces:
źródło
OPTION
w moje zapytanie) i nie skrzywdziłby mnie zbytnio, ponieważ ten sproc jest dobrze wykorzystany w testach integracyjnych. - W każdym razie: dzięki za wgląd!