To pierwsza część serii o prostej parametryzacji i trywialne plany . Te dwie funkcje kompilacji są ściśle powiązane i mają podobne cele. Zarówno wydajność docelowa, jak i wydajność w przypadku obciążeń, które często składają proste oświadczenia.
Pomimo „prostych” i „trywialnych” nazw, oba mają subtelne zachowania i szczegóły implementacji, które mogą utrudniać zrozumienie sposobu ich działania. Ta seria nie skupia się zbyt długo na podstawach, ale koncentruje się na mniej znanych aspektach, które mogą potknąć się nawet o najbardziej doświadczonych specjalistów od baz danych.
W tej pierwszej części, po krótkim wprowadzeniu, przyjrzę się efektom prostej parametryzacji w pamięci podręcznej planu.
Prosta parametryzacja
Prawie zawsze lepiej jest jawnie sparametryzować instrukcji, zamiast polegać na serwerze, aby to zrobił. Jawność zapewnia pełną kontrolę nad wszystkimi aspektami procesu parametryzacji, w tym miejscami użycia parametrów, dokładnymi typami danych i ponownym wykorzystaniem planów.
Większość klientów i sterowników zapewnia określone sposoby użycia jawnej parametryzacji. Istnieją również opcje takie jak sp_executesql
, procedury składowane i funkcje.
Nie zamierzam zagłębiać się w powiązane kwestie związane z podsłuchiwaniem parametrów lub wstrzykiwaniem SQL, ponieważ, choć ważne, nie są one przedmiotem tej serii. Mimo to powinieneś pisać kod tak, aby oba te elementy znajdowały się blisko twojego umysłu.
W przypadku starszych aplikacji lub innego kodu innej firmy, którego nie można łatwo zmienić, jawna parametryzacja nie zawsze jest możliwa. Możesz pokonać niektóre przeszkody, korzystając z przewodników po szablonach. W każdym razie byłoby to nietypowe obciążenie, które nie zawierałoby przynajmniej niektórych sparametryzowanych instrukcji po stronie serwera.
Plany powłoki
Kiedy SQL Server 2005 wprowadził parametryzację wymuszoną , istniejąca automatyczna parametryzacja nazwa funkcji została zmieniona na Prosta parametryzacja . Pomimo zmiany terminologii prosta parametryzacja działa tak samo jak automatyczna parametryzacja zawsze robił:SQL Server próbuje zastąpić stałe wartości literału w instrukcjach ad hoc znacznikami parametrów. Celem jest zmniejszenie liczby kompilacji poprzez zwiększenie ponownego wykorzystania planów z pamięci podręcznej.
Spójrzmy na przykład, używając bazy danych Stack Overflow 2010 na SQL Server 2019 CU 14. Kompatybilność bazy danych jest ustawiona na 150, a próg kosztu dla równoległości jest ustawiony na 50, aby na razie uniknąć równoległości:
EXECUTE sys.sp_configure @configname = 'show advanced options', @configvalue = 1; RECONFIGURE; GO EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 50; RECONFIGURE;
Przykładowy kod:
-- Clear the cache of plans for this database ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 2521; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 2827; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 3144; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 3151; GO
Wypowiedzi te zawierają predykaty, które różnią się tylko stałymi wartościami dosłownymi. SQL Server pomyślnie stosuje prostą parametryzację , co skutkuje sparametryzowanym planem. Pojedynczy sparametryzowany plan jest używany cztery razy, jak możemy zobaczyć, odpytując pamięć podręczną planu:
SELECT CP.usecounts, CP.cacheobjtype, CP.objtype, CP.size_in_bytes, ST.[text], QP.query_plan FROM sys.dm_exec_cached_plans AS CP OUTER APPLY sys.dm_exec_sql_text (CP.plan_handle) AS ST OUTER APPLY sys.dm_exec_query_plan (CP.plan_handle) AS QP WHERE ST.[text] NOT LIKE '%dm_exec_cached_plans%' AND ST.[text] LIKE '%DisplayName%Users%' ORDER BY CP.usecounts ASC;
Wyniki pokazują Adhoc zaplanuj wpis w pamięci podręcznej dla każdego oryginalnego oświadczenia i jednego Przygotowane plan:
Cztery plany ad hoc i jeden przygotowany
Przygotowane Instrukcja jest podobna do procedury składowanej, z parametrami wywnioskowanymi z wartości dosłownych znalezionych w Adhoc oświadczenie. Wspominam o tym jako o przydatnym modelu mentalnym, gdy myślę o procesie parametryzacji po stronie serwera.
Zwróć uwagę, że SQL Server buforuje oba oryginalny tekst i sparametryzowaną formę. Gdy prosta parametryzacja się powiedzie, plan powiązany z oryginalnym tekstem to Adhoc i nie zawiera pełnego planu wykonania. Zamiast tego plan w pamięci podręcznej to powłoka niewiele poza wskaźnikiem do Przygotowane Sparametryzowany plan.
Reprezentacja XML planów powłoki zawierać tekst taki jak:
<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.539" Build="15.0.4188.2"> <BatchSequence> <Batch> <Statements> <StmtSimple StatementText="SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151" StatementId="1" StatementCompId="1" StatementType="SELECT" RetrievedFromCache="true" ParameterizedPlanHandle="0x0600050090C8321CE04B4B079E01000001000000000000000000000000000000000000000000000000000000" ParameterizedText="(@1 smallint)SELECT [U].[DisplayName] FROM [dbo].[Users] [U] WHERE [U].[Reputation]=@1" /> </Statements> </Batch> </BatchSequence> </ShowPlanXML>
To cały plan. Sparametryzowany uchwyt planu punkty z Adhoc powłoki do pełnego sparametryzowanego planu. Wartość uchwytu jest taka sama dla wszystkich czterech planów powłoki.
Wycinki planu
Plany powłoki są mniejsze niż w pełni skompilowany plan — w przykładzie 16 KB zamiast 40 KB. Może to nadal zwiększać znaczną ilość pamięci, jeśli masz wiele instrukcji używających prostej parametryzacji lub wielu różnych wartości parametrów. Większość instancji SQL Server nie jest tak zalana pamięcią, że mogą sobie pozwolić na jej marnowanie w ten sposób. Plany powłoki są uważane przez SQL Server za bardzo jednorazowe, ale ich znajdowanie i usuwanie zużywa zasoby i może stać się punktem spornym.
Możemy zmniejszyć całkowite zużycie pamięci dla planów powłoki, włączając opcję optymalizacji pod kątem obciążeń ad hoc.
EXECUTE sys.sp_configure @configname = 'show advanced options', @configvalue = 1; RECONFIGURE; GO EXECUTE sys.sp_configure @configname = 'optimize for ad hoc workloads', @configvalue = 1; RECONFIGURE;
Powoduje to buforowanie małego kodu pośredniczącego przy pierwszym napotkaniu instrukcji ad hoc zamiast powłoki. Kod pośredniczący służy jako zakładka, dzięki czemu serwer może zapamiętać, że widział wcześniej dokładny tekst instrukcji. Po ponownym napotkaniu tego samego tekstu kompilacja i buforowanie przebiegają tak, jak przy optymalizacji pod kątem obciążeń ad hoc nie zostały włączone.
Ponowne uruchomienie przykładu z opcją optymalizacji pod kątem obciążeń ad hoc włączony pokazuje wpływ na pamięć podręczną planu.
Skompilowane części planu
Żaden plan nie jest buforowany dla instrukcji ad hoc, tylko skrót. Nie ma sparametryzowanego uchwytu planu wskaźnik do Przygotowane plan, chociaż kompletny sparametryzowany plan jest w pamięci podręcznej.
Uruchomienie partii testowych po raz drugi (bez czyszczenia pamięci podręcznej planu) daje taki sam wynik, jak w przypadku optymalizacji pod kątem obciążeń ad hoc nie została włączona — cztery Adhoc plany powłoki wskazujące na Przygotowane plan.
Zanim przejdziesz dalej, zresetuj optymalizację pod kątem obciążeń ad hoc ustawienie na zero:
EXECUTE sys.sp_configure @configname = 'optimize for ad hoc workloads', @configvalue = 0; RECONFIGURE;
Ograniczenia rozmiaru pamięci podręcznej planu
Niezależnie od tego, czy używane są powłoki planu, czy skróty planu, wszystkie te Adhoc mają wady wpisy w pamięci podręcznej. Wspomniałem już o całkowitym wykorzystaniu pamięci, ale każda pamięć podręczna planu ma również maksymalną liczbę wpisów. Nawet tam, gdzie całkowite zużycie pamięci nie jest problemem, sama ilość może być.
Limity można podnieść za pomocą udokumentowanej flagi śledzenia 174 (liczba wpisów) i flagi śledzenia 8032 (całkowity rozmiar). W zależności od obciążenia i innych wymagań dotyczących pamięci może to nie być najlepsze rozwiązanie. W końcu oznacza to po prostu buforowanie większej ilości małowartościowych Adhoc plany, odciągając pamięć od innych potrzeb.
Buforowanie tylko przygotowanych planów
Jeśli obciążenie rzadko generuje partie ad hoc z dokładnie ten sam tekst instrukcji, buforowanie powłok planu lub kodów pośredniczących planu jest marnowaniem zasobów. Zużywa pamięć i może powodować rywalizację, gdy Plany SQL magazyn pamięci podręcznej (CACHESTORE_SQLCP
) należy zmniejszyć, aby zmieścić się w skonfigurowanych limitach.
Idealnie byłoby sparametryzować przychodzące partie ad hoc, ale tylko buforuje sparametryzowaną wersję. Wiąże się to z kosztami, ponieważ przyszłe instrukcje ad-hoc muszą być sparametryzowane, zanim będą mogły zostać dopasowane do sparametryzowanego planu w pamięci podręcznej. Z drugiej strony i tak by się to stało, ponieważ określiliśmy już dokładnie dopasowania tekstowe są rzadkie w przypadku docelowego obciążenia.
W przypadku obciążeń, które korzystają z prostej parametryzacji, ale nie buforowania Adhoc wpisy, istnieje kilka opcji.
Flaga nieudokumentowanego śledzenia
Pierwszą opcją jest włączenie flagi nieudokumentowanego śledzenia 253. To zapobiega buforowanie Adhoc plany całkowicie. Nie ogranicza to po prostu liczby takich planów ani nie zapobiega ich „pozostawaniu” w pamięci podręcznej, jak czasami sugerowano.
Flagę śledzenia 253 można włączyć na poziomie sesji — ograniczając jej efekty tylko do tego połączenia — lub szerzej jako flagę globalną lub flagę startową. Funkcjonuje również jako wskazówka do zapytania, ale ich użycie uniemożliwia prostą parametryzację, która przyniosłaby efekt przeciwny do zamierzonego. Istnieje częściowa lista rzeczy, które uniemożliwiają prostą parametryzację w Microsoft Technical Paper, Plan Caching and Recompilation in SQL Server 2012.
Z flagą śledzenia 253 aktywną przed skompilowaniem partii , tylko Przygotowane wyciągi są buforowane:
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO -- Do not cache ad-hoc plans DBCC TRACEON (253); GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 2521; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 2827; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 3144; GO SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 3151; GO -- Cache ad-hoc plans again DBCC TRACEOFF (253); GO
Zapytanie o pamięć podręczną planu potwierdza tylko Przygotowane instrukcja jest buforowana i ponownie używana.
Tylko przygotowana instrukcja jest buforowana
Pakiet, którego nie można buforować
Drugą opcją jest dołączenie oświadczenia, które oznacza całą wiązkę jako niedostępną . Odpowiednie stwierdzenia są często związane z bezpieczeństwem lub w inny sposób wrażliwe.
Może to brzmieć niepraktycznie, ale istnieje kilka łagodzeń. Po pierwsze, poufne oświadczenie nie musi być wykonywane — wystarczy, że jest obecne . Gdy ten warunek jest spełniony, użytkownik uruchamiający partię nie potrzebuje nawet pozwolenia do wykonania wrażliwej instrukcji. Zwróć uwagę, że efekt jest ograniczony do partii zawierającej poufne oświadczenie.
Poniżej przedstawiono dwa odpowiednio wrażliwe oświadczenia i przykładowe użycie (z oświadczeniami testowymi teraz w jednej partii):
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE; GO -- Prevent caching of all statements in this batch. -- Neither KEY nor CERTIFICATE need to exist. -- No special permissions are needed. -- GOTO is used to ensure the statements are not executed. GOTO Start OPEN SYMMETRIC KEY Banana DECRYPTION BY CERTIFICATE Banana; Start: /* Another way to achieve the same effect without GOTO IF 1 = 0 BEGIN CREATE APPLICATION ROLE Banana WITH PASSWORD = ''; END; */ SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 2521; SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 2827; SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 3144; SELECT U.DisplayName FROM dbo.Users AS U WHERE U.Reputation = 3151; GO
Przygotowane plany utworzone przez prostą parametryzację są nadal buforowane i używane ponownie, mimo że partia nadrzędna została oznaczona jako niemożliwa do zbuforowania.
Tylko przygotowana instrukcja jest buforowana
Żadne z rozwiązań nie jest idealne, ale dopóki Microsoft nie dostarczy udokumentowanego i obsługiwanego rozwiązania tego problemu, są to najlepsze opcje, jakie znam.
Koniec części 1
Na ten temat jest dużo więcej informacji. Część druga omówi typy danych przypisywane podczas prostej parametryzacji jest zatrudniony.