sql >> Database teknologi >  >> RDS >> Database

Simpel parametrering og trivielle planer — del 1

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&#xD;&#xA;FROM dbo.Users AS U &#xD;&#xA;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.


  1. Hvad er nyt i PostgreSQL 11

  2. Fuzzy matching ved hjælp af T-SQL

  3. postgresql:datatype for md5-meddelelsessammendrag?

  4. gem install pg --with-pg-config virker, bundt mislykkes