Plany wykonania
Jest to bardziej skomplikowane, niż można by się spodziewać na podstawie informacji zawartych w planach wykonania, jeśli instrukcja SQL używa prostej parametryzacji . Nic dziwnego, że nawet bardzo doświadczeni użytkownicy SQL Server mają tendencję do popełniania błędów, biorąc pod uwagę często dostarczane nam sprzeczne informacje.
Spójrzmy na kilka przykładów wykorzystujących bazę danych Stack Overflow 2010 na SQL Server 2019 CU 14, z kompatybilnością bazy danych ustawioną na 150.
Na początek potrzebujemy nowego indeksu nieklastrowanego:
CREATE INDEX [IX dbo.Users Reputation (DisplayName)] ON dbo.Users (Reputation) INCLUDE (DisplayName);
1. Zastosowano prostą parametryzację
To pierwsze przykładowe zapytanie wykorzystuje prostą parametryzację :
SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 999;
szacowana Plan (przedwykonawczy) ma następujące elementy związane z parametryzacją:
Właściwości szacunkowej parametryzacji planu
Zwróć uwagę na @1
parametr jest wprowadzany wszędzie z wyjątkiem tekstu zapytania wyświetlanego u góry.
rzeczywista (powykonawczy) plan ma:
Rzeczywiste właściwości parametryzacji planu
Zauważ, że okno właściwości straciło teraz ParameterizedText
element, jednocześnie pozyskując informacje o wartości parametru runtime. Sparametryzowany tekst zapytania jest teraz wyświetlany w górnej części okna za pomocą „@1
’ zamiast ‘999’.
2. Nie zastosowano prostej parametryzacji
Ten drugi przykład nie użyj prostej parametryzacji:
-- Projecting an extra column SELECT U.DisplayName, U.CreationDate -- NEW FROM dbo.Users AS U WHERE U.Reputation = 999;
szacowana plan pokazuje:
Szacowany plan niesparametryzowany
Tym razem parametr @1
brakuje w poszukiwaniu indeksu podpowiedź, ale sparametryzowany tekst i inne elementy listy parametrów są takie same jak wcześniej.
Spójrzmy na rzeczywiste plan wykonania:
Rzeczywisty plan niesparametryzowany
Wyniki są takie same jak w przypadku poprzedniej sparametryzowanej rzeczywistej plan, z wyjątkiem teraz Poszukiwania indeksu podpowiedź wyświetla niesparametryzowaną wartość „999”. Tekst zapytania pokazany u góry używa @1
znacznik parametru. Okno właściwości używa również @1
i wyświetla wartość parametru w czasie wykonywania.
Zapytanie nie jest sparametryzowaną instrukcją pomimo wszystkich dowodów, że jest inaczej.
3. Parametryzacja nie powiodła się
Mój trzeci przykład to również nie sparametryzowany przez serwer:
-- LOWER function used SELECT U.DisplayName, LOWER(U.DisplayName) FROM dbo.Users AS U WHERE U.Reputation = 999;
szacowana plan to:
Szacowana parametryzacja planu nie powiodła się
Nie ma wzmianki o @1
parametr w dowolnym miejscu i Lista parametrów brakuje sekcji okna właściwości.
rzeczywista plan wykonania jest taki sam, więc nie zawracam sobie głowy pokazywaniem go.
4. Równoległy plan sparametryzowany
Chcę wam pokazać jeszcze jeden przykład wykorzystania paralelizmu w planie wykonania. Niski szacowany koszt moich zapytań testowych oznacza, że musimy obniżyć próg kosztów dla równoległości do 1:
EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 1; RECONFIGURE;
Przykład jest tym razem nieco bardziej złożony:
SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation >= 5 AND U.DisplayName > N'ZZZ' ORDER BY U.Reputation DESC;
szacowana plan wykonania to:
Szacowany sparametryzowany plan równoległy
Tekst zapytania u góry pozostaje niesparametryzowany, podczas gdy wszystko inne jest. Są teraz dwa znaczniki parametrów, @1
i @2
, ponieważ prosta parametryzacja znalazł dwie odpowiednie wartości dosłowne.
rzeczywista plan wykonania jest zgodny ze wzorem z przykładu 1:
Rzeczywisty sparametryzowany plan
Tekst zapytania u góry jest teraz sparametryzowany, a okno właściwości zawiera wartości parametrów środowiska wykonawczego. Ten równoległy plan (z Sortuj operator) jest zdecydowanie sparametryzowany przez serwer za pomocą prostej parametryzacji .
Niezawodne metody
Istnieją powody wszystkich dotychczasowych zachowań, a poza tym kilka innych. Spróbuję wyjaśnić wiele z nich w następnej części tej serii, kiedy omówię kompilację planów.
W międzyczasie sytuacja z showplanem w ogóle, aw szczególności z SSMS, nie jest idealna. Jest to mylące dla osób, które przez całą swoją karierę pracowały z SQL Server. Którym znacznikom parametrów ufasz, a które ignorujesz?
Istnieje kilka niezawodnych metod określania, czy dana instrukcja została pomyślnie zastosowana do prostej parametryzacji, czy nie.
Zapis zapytań
Zacznę od jednego z najwygodniejszych, sklepu z zapytaniami. Niestety, nie zawsze jest to tak proste, jak możesz sobie wyobrazić.
Musisz włączyć funkcję magazynu zapytań dla kontekstu bazy danych gdzie instrukcja jest wykonywana i OPERATION_MODE
musi być ustawiony na READ_WRITE
, umożliwiając magazynowi zapytań aktywne zbieranie danych.
Po spełnieniu tych warunków dane wyjściowe showplanu po wykonaniu zawierają dodatkowe atrybuty, w tym StatementParameterizationType . Jak sama nazwa wskazuje, zawiera kod opisujący rodzaj parametryzacji użytej w instrukcji.
Jest to widoczne w oknie właściwości SSMS po wybraniu węzła głównego planu:
StatementParameterizationType
Wartości są udokumentowane w sys.query_store_query
:
- 0 – brak
- 1 – Użytkownik (jawna parametryzacja)
- 2 – Prosta parametryzacja
- 3 – Wymuszona parametryzacja
Ten korzystny atrybut pojawia się w SSMS tylko wtedy, gdy rzeczywista plan jest wymagany i brakuje go, gdy szacowany plan jest wybrany. Ważne jest, aby pamiętać, że plan musi być buforowany . Prośba o szacunkową plan z SSMS nie buforuje utworzonego planu (od SQL Server 2012).
Gdy plan zostanie zapisany w pamięci podręcznej, StatementParameterizationType pojawia się w zwykłych miejscach, w tym przez sys.dm_exec_query_plan
.
Możesz również zaufać innym miejscom, w których typ parametryzacji jest rejestrowany w magazynie zapytań, na przykład query_parameterization_type_desc
kolumna w sys.query_store_query
.
Jedno ważne zastrzeżenie. Gdy zapytanie przechowuje OPERATION_MODE
jest ustawiony na READ_ONLY
, StatementParameterizationType atrybut jest nadal wypełniany w SSMS rzeczywisty plany — ale zawsze jest zero — dając fałszywe wrażenie, że stwierdzenie nie zostało sparametryzowane, chociaż mogło tak być.
Jeśli jesteś zadowolony z włączenia magazynu zapytań, masz pewność, że jest do odczytu i zapisu i patrzysz tylko na plany powykonawcze w SSMS, to zadziała dla Ciebie.
Standardowe predykaty planu
Tekst zapytania wyświetlany w górnej części okna graficznego planu pokazu w programie SSMS nie jest niezawodny, jak pokazano na przykładach. Nie możesz też polegać na ParameterList wyświetlane w Właściwościach okno po wybraniu węzła głównego planu. Tekst sparametryzowany atrybut pokazany dla szacowany tylko plany nie są rozstrzygające.
Możesz jednak polegać na właściwościach powiązanych z poszczególnymi operatorami planu. Podane przykłady pokazują, że są one obecne w podpowiedziach po najechaniu kursorem na operatora.
Predykat zawierający znacznik parametru, taki jak @1
lub @2
wskazuje sparametryzowany plan. Operatory, które najprawdopodobniej zawierają parametr to Skanowanie indeksu , Przeszukiwanie indeksu i Filtr .
Predykaty ze znacznikami parametrów
Jeśli numeracja zaczyna się od @1
, używa prostej parametryzacji . Wymuszona parametryzacja zaczyna się od @0
. Powinienem wspomnieć, że udokumentowany tutaj schemat numeracji może ulec zmianie w dowolnym momencie:
Ostrzeżenie o zmianie
Niemniej jednak jest to metoda, której używam najczęściej w celu ustalenia, czy plan podlegał parametryzacji po stronie serwera. Generalnie szybkie i łatwe jest wizualne sprawdzenie planu pod kątem predykatów zawierających znaczniki parametrów. Ta metoda działa również w przypadku obu rodzajów planów, szacowanych i rzeczywiste .
Dynamiczne obiekty zarządzania
Istnieje kilka sposobów sprawdzania pamięci podręcznej planu i powiązanych obiektów DMO w celu określenia, czy instrukcja została sparametryzowana. Oczywiście te zapytania działają tylko na planach w pamięci podręcznej, więc instrukcja musi zostać wykonana do końca, zbuforowana, a następnie z jakiegokolwiek powodu wykluczona.
Najbardziej bezpośrednim podejściem jest szukanie adhoc planuj, używając dokładnego dopasowania tekstowego SQL do interesującego Cię zestawienia. Adhoc plan będzie powłoką zawierający SparameterizedPlanHandle jeśli instrukcja jest sparametryzowana przez serwer. Uchwyt planu jest następnie używany do zlokalizowania Przygotowane plan. Adhoc plan nie będzie istniał, jeśli włączono optymalizację pod kątem obciążeń ad hoc, a dana instrukcja została wykonana tylko raz.
Tego typu zapytanie często kończy się zniszczeniem znacznej ilości XML i przynajmniej raz skanowaniem całej pamięci podręcznej planu. Łatwo też pomylić kod, nie tylko dlatego, że plany w pamięci podręcznej obejmują całą partię. Partia może zawierać wiele instrukcji, z których każda może być sparametryzowana lub nie. Nie wszystkie DMO działają na tym samym poziomie szczegółowości (partii lub zestawienia), co sprawia, że dość łatwo można się oderwać.
Skuteczny sposób na wypisanie interesujących stwierdzeń wraz z fragmentami planu tylko dla tych pojedynczych stwierdzeń pokazano poniżej:
SELECT StatementText = SUBSTRING(T.[text], 1 + (QS.statement_start_offset / 2), 1 + ((QS.statement_end_offset - QS.statement_start_offset) / 2)), IsParameterized = IIF(T.[text] LIKE N'(%', 'Yes', 'No'), query_plan = TRY_CONVERT(xml, P.query_plan) FROM sys.dm_exec_query_stats AS QS CROSS APPLY sys.dm_exec_sql_text (QS.[sql_handle]) AS T CROSS APPLY sys.dm_exec_text_query_plan ( QS.plan_handle, QS.statement_start_offset, QS.statement_end_offset) AS P WHERE -- Statements of interest T.[text] LIKE N'%DisplayName%Users%' -- Exclude queries like this one AND T.[text] NOT LIKE N'%sys.dm%' ORDER BY QS.last_execution_time ASC, QS.statement_start_offset ASC;
Aby to zilustrować, uruchommy pojedynczą partię zawierającą cztery przykłady z wcześniejszych:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO -- Example 1 SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 999; -- Example 2 SELECT U.DisplayName, U.CreationDate FROM dbo.Users AS U WHERE U.Reputation = 999; -- Example 3 SELECT U.DisplayName, LOWER(U.DisplayName) FROM dbo.Users AS U WHERE U.Reputation = 999; -- Example 4 SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation >= 5 AND U.DisplayName > N'ZZZ' ORDER BY U.Reputation DESC; GO
Dane wyjściowe zapytania DMO to:
Wyjście zapytania DMO
Potwierdza to, że tylko przykłady 1 i 4 zostały pomyślnie sparametryzowane.
Liczniki wydajności
Możliwe jest użycie liczników wydajności SQL Statistics, aby uzyskać szczegółowy wgląd w aktywność parametryzacji dla obu szacowanych i rzeczywiste plany. Użyte liczniki nie są objęte zakresem na sesję, więc aby uzyskać dokładne wyniki, musisz użyć instancji testowej bez innej jednoczesnej aktywności.
Uzupełnię informacje o liczniku parametryzacji danymi z sys.dm_exec_query_optimizer_info
DMO dostarcza również statystyki dotyczące trywialnych planów.
Należy zachować ostrożność, aby instrukcje odczytujące informacje o licznikach nie modyfikowały samych tych liczników. Zajmę się tym, tworząc kilka tymczasowych procedur składowanych:
CREATE PROCEDURE #TrivialPlans AS SET NOCOUNT ON; SELECT OI.[counter], OI.occurrence FROM sys.dm_exec_query_optimizer_info AS OI WHERE OI.[counter] = N'trivial plan'; GO CREATE PROCEDURE #PerfCounters AS SET NOCOUNT ON; SELECT PC.[object_name], PC.counter_name, PC.cntr_value FROM sys.dm_os_performance_counters AS PC WHERE PC.counter_name LIKE N'%Param%';
Skrypt do testowania konkretnej instrukcji wygląda wtedy tak:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO EXECUTE #PerfCounters; EXECUTE #TrivialPlans; GO SET SHOWPLAN_XML ON; GO -- The statement(s) under test: -- Example 3 SELECT U.DisplayName, LOWER(U.DisplayName) FROM dbo.Users AS U WHERE U.Reputation = 999; GO SET SHOWPLAN_XML OFF; GO EXECUTE #TrivialPlans; EXECUTE #PerfCounters;
Skomentuj SHOWPLAN_XML
wysyła partie, aby uruchomić zestawienia docelowe i uzyskać rzeczywistą plany. Zostaw je na miejscu na szacowany plany wykonania.
Uruchomienie całości tak, jak napisano, daje następujące wyniki:
Wyniki testu licznika wydajności
Powyżej zaznaczyłem, gdzie zmieniły się wartości podczas testowania przykładu 3.
Wzrost licznika „trywialnego planu” z 1050 do 1051 pokazuje, że dla instrukcji testowej znaleziono trywialny plan.
Liczniki prostej parametryzacji wzrosły o 1 zarówno dla prób, jak i niepowodzeń, pokazując, że SQL Server próbował sparametryzować instrukcję, ale nie powiodło się.
Koniec części 3
W następnej części tej serii wyjaśnię ciekawe rzeczy, które widzieliśmy, opisując, jak prosta parametryzacja i trywialne plany wchodzić w interakcje z procesem kompilacji.
Jeśli zmieniłeś próg kosztów dla równoległości aby uruchomić przykłady, pamiętaj, aby je zresetować (moje było ustawione na 50):
EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 50; RECONFIGURE;