Detta är den första delen av en serie om enkel parameterisering och triviala planer . Dessa två kompileringsfunktioner är nära sammankopplade och har liknande mål. Både målprestanda och effektivitet för arbetsbelastningar som ofta skickar in enkla uttalanden.
Trots de "enkla" och "triviala" namnen har båda subtila beteenden och implementeringsdetaljer som kan göra hur de fungerar svårt att förstå. Den här serien uppehåller sig inte alltför länge på grunderna utan koncentrerar sig på mindre välkända aspekter som sannolikt kommer att slå upp även de mest erfarna databasproffsen.
I den här första delen, efter en snabb introduktion, tittar jag på effekterna av enkel parameterisering på planens cache.
Enkel parametrering
Det är nästan alltid bättre att explicit parametrisera uttalanden, snarare än att lita på att servern gör det. Att vara tydlig ger dig fullständig kontroll över alla aspekter av parametreringsprocessen, inklusive var parametrar används, vilka exakta datatyper som används och när planer återanvänds.
De flesta klienter och drivrutiner tillhandahåller specifika sätt att använda explicit parameterisering. Det finns också alternativ som sp_executesql
, lagrade procedurer och funktioner.
Jag tänker inte gå in på de relaterade frågorna om parametersniffning eller SQL-injektion eftersom de, även om de är viktiga, inte är i fokus för den här serien. Ändå bör du skriva kod med båda nära ditt huvud.
För äldre applikationer eller annan tredjepartskod som inte enkelt kan ändras, är det inte alltid möjligt att explicit parametrera inställningar. Du kanske kan övervinna vissa hinder med hjälp av mallplansguider. I vilket fall som helst skulle det vara en ovanlig arbetsbelastning som inte innehåller åtminstone några parametriserade satser på serversidan.
Shell-planer
När SQL Server 2005 introducerade Forced Parameterization , den befintliga autoparameteriseringen Funktionen bytte namn till Enkel parametrering . Trots ändrad terminologi, enkel parametrisering fungerar på samma sätt som auto-parameterisering alltid gjort:SQL Server försöker ersätta konstanta bokstavliga värden i ad hoc-satser med parametermarkörer. Syftet är att minska kompileringarna genom att öka cachad planåteranvändning.
Låt oss titta på ett exempel genom att använda databasen Stack Overflow 2010 på SQL Server 2019 CU 14. Databaskompatibilitet är inställd på 150 och kostnadströskeln för parallellitet är satt till 50 för att undvika parallellism för tillfället:
EXECUTE sys.sp_configure @configname = 'show advanced options', @configvalue = 1; RECONFIGURE; GO EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 50; RECONFIGURE;
Exempelkod:
-- 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
Dessa uttalanden har predikat som endast skiljer sig åt i sina konstanta bokstavliga värden. SQL Server tillämpar framgångsrikt enkel parameterisering , vilket resulterar i en parametriserad plan. Den enda parametriserade planen används fyra gånger som vi kan se genom att fråga planens cache:
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;
Resultaten visar en Adhoc planera cache-post för varje originalsats och en enda Förberedd plan:
Fyra adhoc-planer och en utarbetad plan
En Förberedd uttalande liknar en lagrad procedur, med parametrar härledda från bokstavliga värden som finns i Adhoc påstående. Jag nämner detta som en användbar mental modell när jag tänker på parameteriseringsprocessen på serversidan.
Lägg märke till att SQL Server cachar båda originaltexten och den parametriserade formen. När enkel parameterisering lyckas är planen som är kopplad till originaltexten Adhoc och innehåller inte en fullständig genomförandeplan. Istället är den cachade planen ett skal med mycket lite förutom en pekare till Förberedda parametrerad plan.
XML-representationen av skalplanerna innehålla text som:
<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>
Det är hela planen. ParameterizedPlanHandle poäng från Adhoc skal till den fullständiga parametriserade planen. Handtagsvärdet är detsamma för alla fyra skalplanerna.
Planstubbar
Skalplaner är mindre än en fullständig kompilerad plan—16KB istället för 40KB i exemplet. Detta kan fortfarande lägga till upp till en betydande mängd minne om du har många satser som använder enkel parametrering eller många olika parametervärden. De flesta SQL Server-instanser är inte så fulla av minne att de har råd att slösa bort det så här. Skalplanerna anses vara mycket disponibla av SQL Server, men att hitta och ta bort dem förbrukar resurser och kan bli en stridsfråga.
Vi kan minska den totala minnesförbrukningen för skalplaner genom att aktivera alternativet Optimera för ad hoc-arbetsbelastningar.
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;
Detta cachar en liten stubb första gången ett ad hoc-uttalande påträffas istället för ett skal. Stubben fungerar som ett bokmärke så att servern kan komma ihåg att den har sett den exakta texten tidigare. När du stöter på samma text en andra gång fortsätter kompileringen och cachelagringen som om du optimerade för ad hoc-arbetsbelastningar var inte aktiverade.
Kör exemplet igen med optimera för ad hoc-arbetsbelastningar aktiverat visar effekten på planens cache.
Kompilerade planstubbar
Ingen plan är cachad för ad-hoc uttalanden, bara en stubb. Det finns ingen ParameterizedPlanHandle pekare till Förberedda plan, även om en komplett parametriserad plan är cachad.
Att köra testsatserna en andra gång (utan att rensa planens cache) ger samma resultat som när optimera för ad hoc-arbetsbelastningar var inte aktiverat – fyra Adhoc skalplaner som pekar på Förberedda plan.
Innan du fortsätter, återställ optimera för ad hoc-arbetsbelastningar inställning till noll:
EXECUTE sys.sp_configure @configname = 'optimize for ad hoc workloads', @configvalue = 0; RECONFIGURE;
Planera cachestorleksgränser
Oavsett om planskal eller planstubbar används, finns det fortfarande nackdelar med alla dessa Adhoc cacheposter. Jag har redan nämnt total minnesanvändning, men varje plancache har också ett maximalt antal av poster. Även där den totala minnesanvändningen inte är ett problem, kan den stora mängden vara det.
Gränserna kan höjas med dokumenterad spårflagga 174 (antal poster) och spårningsflagga 8032 (total storlek). Beroende på arbetsbelastningen och andra minneskrav kanske detta inte är den bästa lösningen. När allt kommer omkring betyder det bara att cachelagra mer lågvärde Adhoc planer, tar minnet bort från andra behov.
Cachar endast förberedda planer
Om arbetsbelastningen sällan utfärdar ad-hoc-batcher med exakt samma uttalande text, cacheplanskal eller planstubbar är slöseri med resurser. Det förbrukar minne och kan orsaka konflikter när SQL-planerna cachelager (CACHESTORE_SQLCP
) måste krympas för att passa inom konfigurerade gränser.
Det ideala skulle vara att parametrisera inkommande ad-hoc-batcher, men endast cachelagra den parametriserade versionen. Det är en kostnad att göra detta, eftersom framtida ad-hoc-satser måste parametriseras innan de kan matchas med den parameteriserade cachade planen. Å andra sidan skulle detta ha hänt ändå eftersom vi redan har sagt exakt textmatchningar är sällsynta för målarbetsbelastningen.
För arbetsbelastningar som drar nytta av enkel parametrering, men inte cachningen av Adhoc poster finns det ett par alternativ.
Odokumenterad spårningsflagga
Det första alternativet är att aktivera odokumenterad spårningsflagga 253. Detta förhindrar cachningen av Adhoc planerar helt. Det begränsar inte bara antalet sådana planer eller hindrar dem från att "bli" i cachen, som ibland har föreslagits.
Spårningsflagga 253 kan aktiveras på sessionsnivå – vilket begränsar dess effekter till just den anslutningen – eller mer allmänt som en global flagga eller startflagga. Det fungerar också som ett frågetips, men att använda dem förhindrar enkel parameterisering, vilket skulle vara kontraproduktivt här. Det finns en ofullständig lista över saker som förhindrar enkel parametrering i Microsofts tekniska dokument, plancachning och omkompilering i SQL Server 2012.
Med spårningsflagga 253 aktiv innan batchen kompileras , endast de Förberedda uttalanden cachelagras:
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
Planens cachefråga bekräftar endast Förberedda uttalandet cachelagras och återanvänds.
Endast den förberedda satsen cachelagras
Den uncacheable batchen
Det andra alternativet är att inkludera en sats som markerar hela batchen som uncacheable . Lämpliga uttalanden är ofta säkerhetsrelaterade eller på annat sätt känsliga på något sätt.
Detta kan låta opraktiskt, men det finns ett par begränsningar. För det första behöver den känsliga satsen inte köras – den behöver bara vara närvarande . När det villkoret är uppfyllt behöver användaren som kör batchen inte ens behörighet för att utföra det känsliga uttalandet. Observera noga, effekten är begränsad till den batch som innehåller det känsliga uttalandet.
Två lämpligt känsliga uttalanden och exempel på användning visas nedan (med testsatserna nu i en enda batch):
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
Den Förberedda planer skapade av enkel parameterisering är fortfarande cachade och återanvänds trots att den överordnade batchen är markerad som ocachebar.
Endast den förberedda satsen cachelagras
Ingen av lösningarna är idealiska, men tills Microsoft tillhandahåller en dokumenterad och stödd lösning för det här problemet är de de bästa alternativen jag känner till.
Slutet av del 1
Det finns mycket mer att täcka om detta ämne. Del två kommer att täcka de datatyper som tilldelas vid enkel parameterisering är anställd.