Dette er første del af en serie om simpel parameterisering og trivielle planer . Disse to kompileringsfunktioner er tæt forbundet og har lignende mål. Både målpræstation og effektivitet for arbejdsbelastninger, der ofte sender simple erklæringer.
På trods af de "simple" og "trivielle" navne har begge subtil adfærd og implementeringsdetaljer, der kan gøre, hvordan de arbejder, svært at forstå. Denne serie dvæler ikke for længe ved det grundlæggende, men koncentrerer sig om mindre kendte aspekter, der sandsynligvis vil slå selv de mest erfarne databaseprofessionelle i øjnene.
I denne første del ser jeg efter en hurtig introduktion på effekterne af simpel parameterisering på planens cache.
Simpel parametrering
Det er næsten altid bedre at parametrisere eksplicit udsagn, i stedet for at stole på, at serveren gør det. At være eksplicit giver dig fuld kontrol over alle aspekter af parametreringsprocessen, inklusive hvor parametre bruges, de præcise datatyper der anvendes, og hvornår planer genbruges.
De fleste klienter og drivere giver specifikke måder at bruge eksplicit parameterisering på. Der er også muligheder som sp_executesql
, lagrede procedurer og funktioner.
Jeg vil ikke komme ind på de relaterede problemer med parametersniffing eller SQL-injektion, fordi selvom de er vigtige, er de ikke i fokus i denne serie. Alligevel bør du skrive kode med begge tæt på hovedet af dit sind.
For ældre applikationer eller anden tredjepartskode, som ikke let kan ændres, er eksplicit parameterisering muligvis ikke altid mulig. Du kan muligvis overvinde nogle forhindringer ved at bruge skabelonplanvejledninger. Under alle omstændigheder ville det være en usædvanlig arbejdsbyrde, der ikke indeholder i det mindste nogle parametriserede sætninger på serversiden.
Shell-planer
Da SQL Server 2005 introducerede Forced Parameterization , den eksisterende auto-parameterisering funktion blev omdøbt til Simple Parameterization . På trods af ændringen i terminologi, simpel parameterisering fungerer på samme måde som auto-parameterisering har altid gjort:SQL Server forsøger at erstatte konstante bogstavelige værdier i ad hoc-sætninger med parametermarkører. Målet er at reducere kompileringer ved at øge cachelagret plangenbrug.
Lad os se på et eksempel ved at bruge Stack Overflow 2010-databasen på SQL Server 2019 CU 14. Databasekompatibilitet er sat til 150, og omkostningstærsklen for parallelitet er sat til 50 for at undgå parallelisme i øjeblikket:
EXECUTE sys.sp_configure @configname = 'show advanced options', @configvalue = 1; RECONFIGURE; GO EXECUTE sys.sp_configure @configname = 'cost threshold for parallelism', @configvalue = 50; RECONFIGURE;
Eksempelkode:
-- 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
Disse udsagn har prædikater, der kun adskiller sig i deres konstante bogstavelige værdier. SQL Server anvender simpel parameterisering , hvilket resulterer i en parametriseret plan. Den enkelt parameteriserede plan bruges fire gange, som vi kan se ved at forespørge i plancachen:
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;
Resultaterne viser en Adhoc plan cache-indgang for hver original erklæring og en enkelt Forberedt plan:
Fire adhoc-planer og én forberedt plan
A Forberedt sætning ligner en lagret procedure med parametre udledt af bogstavelige værdier fundet i Adhoc udmelding. Jeg nævner dette som en nyttig mental model, når jeg tænker på parameteriseringsprocessen på serversiden.
Bemærk, at SQL Server cacher begge den originale tekst og den parametriserede form. Når simpel parameterisering er vellykket, er planen forbundet med den originale tekst Adhoc og indeholder ikke en fuldstændig udførelsesplan. I stedet er den cachelagrede plan en skal med meget lidt udover en pegepind til Forberedt parametriseret plan.
XML-repræsentationen af shell-planerne indeholde tekst 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 er hele planen. ParameterizedPlanHandle point fra Adhoc skal til den fulde parametriserede plan. Håndtagsværdien er den samme for alle fire shell-planer.
Planstubs
Shell-planer er mindre end en komplet kompileret plan - 16 KB i stedet for 40 KB i eksemplet. Dette kan stadig tilføje op til en betydelig mængde hukommelse, hvis du har mange udsagn ved hjælp af simpel parameterisering eller mange forskellige parameterværdier. De fleste SQL Server-instanser er ikke så fyldt med hukommelse, at de har råd til at spilde det på denne måde. Shell-planerne betragtes som meget disponible af SQL Server, men at finde og fjerne dem bruger ressourcer og kan blive et stridspunkt.
Vi kan reducere det samlede hukommelsesforbrug for shell-planer ved at aktivere muligheden for at optimere til ad hoc-arbejdsbelastninger.
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;
Dette cacherer en lille stub første gang en ad hoc-sætning stødes på i stedet for en shell. Stubben fungerer som et bogmærke, så serveren kan huske, at den har set den nøjagtige erklæringstekst før. Når du støder på den samme tekst en anden gang, fortsætter kompilering og cachelagring, som om du optimerer til ad hoc-arbejdsbelastninger var ikke aktiveret.
Genkørsel af eksemplet med optimer til ad hoc-arbejdsbelastninger aktiveret viser effekten på planens cache.
Kompilerede planstubber
Ingen plan er cachelagret for ad hoc-udtalelserne, kun en stump. Der er ingen ParameterizedPlanHandle markøren til Forberedt plan, selvom en komplet parametriseret plan er cachelagret.
Kørsel af testbatchene for anden gang (uden at rydde planens cache) giver det samme resultat, som når du optimerer til ad hoc-arbejdsbelastninger var ikke aktiveret – fire Adhoc shell-planer, der peger på Forberedt plan.
Inden du fortsætter, skal du nulstille optimer til ad hoc-arbejdsbelastninger indstilling til nul:
EXECUTE sys.sp_configure @configname = 'optimize for ad hoc workloads', @configvalue = 0; RECONFIGURE;
Grænser for plan cachestørrelse
Uanset om der bruges planskaller eller planstubber, er der stadig ulemper ved alle disse Adhoc cacheposter. Jeg har allerede nævnt samlet hukommelsesbrug, men hver plan-cache har også et maksimalt antal af indgange. Selv hvor det samlede hukommelsesforbrug ikke er et problem, kan den store mængde være det.
Grænserne kan hæves med dokumenteret sporflag 174 (antal tilmeldinger) og sporflag 8032 (total størrelse). Afhængigt af arbejdsbyrden og andre hukommelseskrav er dette muligvis ikke den bedste løsning. Når alt kommer til alt, betyder det bare at cache mere Adhoc med lav værdi planer, der tager hukommelsen væk fra andre behov.
Caching kun af forberedte planer
Hvis arbejdsbyrden sjældent udsender ad-hoc batches med præcis den samme erklæringstekst, cacheplanskaller eller planstubber er spild af ressourcer. Det bruger hukommelse og kan forårsage uenighed, når SQL-planerne cachelager (CACHESTORE_SQLCP
) skal krympes for at passe inden for konfigurerede grænser.
Det ideelle ville være at parametrere indgående ad-hoc batches, men kun cache den parametrerede version. Det er en omkostning ved at gøre dette, fordi fremtidige ad-hoc-sætninger skal parametreres, før de kan matches med den parametrerede cachelagrede plan. På den anden side ville dette være sket alligevel, da vi allerede har angivet præcis tekstlige matches er sjældne for målarbejdsbyrden.
Til arbejdsbelastninger, der drager fordel af simpel parameterisering, men ikke caching af Adhoc poster, er der et par muligheder.
Udokumenteret sporingsflag
Den første mulighed er at aktivere udokumenteret sporingsflag 253. Dette forhindrer cachen af Adhoc planer fuldstændigt. Det begrænser ikke blot antallet af sådanne planer eller forhindrer dem i at "blive" i cachen, som det nogle gange er blevet foreslået.
Sporingsflag 253 kan aktiveres på sessionsniveau - hvilket begrænser dets virkninger til netop den forbindelse - eller mere bredt som et globalt flag eller opstartsflag. Det fungerer også som et forespørgselstip, men at bruge dem forhindrer simpel parameterisering, hvilket ville være kontraproduktivt her. Der er en delvis liste over de ting, der forhindrer simpel parameterisering, i Microsofts tekniske papir, plancaching og genkompilering i SQL Server 2012.
Med sporingsflag 253 aktivt før batchen kompileres , kun de Forberedte sætninger cachelagres:
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
Plancache-forespørgslen bekræfter kun Forberedt sætningen cachelagres og genbruges.
Kun den forberedte sætning er cachelagret
Den uncacheable batch
Den anden mulighed er at inkludere en erklæring, der markerer hele batchen som ikke-cachebar . Egnede udsagn er ofte sikkerhedsrelaterede eller på anden måde følsomme på en eller anden måde.
Det lyder måske upraktisk, men der er et par begrænsninger. For det første behøver den følsomme sætning ikke at blive udført – den skal bare være til stede . Når denne betingelse er opfyldt, behøver brugeren, der kører batchen, ikke engang tilladelse at udføre den følsomme erklæring. Bemærk omhyggeligt, effekten er begrænset til den batch, der indeholder den følsomme erklæring.
To passende følsomme udsagn og eksempler på brug er vist nedenfor (med testudsagn nu i en enkelt 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 Forberedte planer oprettet ved simpel parameterisering er stadig cachelagret og genbrugt på trods af, at den overordnede batch er markeret som uncacheable.
Kun den forberedte sætning er cachelagret
Ingen af løsningerne er ideelle, men indtil Microsoft leverer en dokumenteret og understøttet løsning til dette problem, er de de bedste muligheder, jeg kender.
Slut på del 1
Der er meget mere jord at dække om dette emne. Del to vil dække de datatyper, der tildeles ved simpel parameterisering er ansat.