Database
 sql >> Baza danych >  >> RDS >> Database

Prosta parametryzacja i trywialne plany — część 3

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;

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Usuwanie domyślnego śladu – część 3

  2. Dołącz do 3 tabel w SQL

  3. Jak uzyskać dzień z daty w T-SQL

  4. Hekaton z niespodzianką:In-memory TVP – część 2

  5. Wpływ na wydajność różnych technik obsługi błędów