sql >> Database >  >> RDS >> Database

Eenvoudige parametrering en triviale plannen - deel 1

Dit is het eerste deel van een serie over eenvoudige parametrering en triviale plannen . Deze twee compilatiefuncties zijn nauw met elkaar verbonden en hebben vergelijkbare doelen. Zowel doelprestaties als efficiëntie voor workloads die vaak eenvoudige verklaringen indienen.

Ondanks de "eenvoudige" en "triviale" namen, hebben beide subtiele gedragingen en implementatiedetails die de manier waarop ze werken moeilijk te begrijpen kunnen maken. Deze serie blijft niet te lang stilstaan ​​bij de basis, maar concentreert zich op minder bekende aspecten die zelfs de meest ervaren databaseprofessionals zullen laten struikelen.

In dit eerste deel bekijk ik, na een korte introductie, de effecten van eenvoudige parametrering op de plancache.

Eenvoudige parametrering

Het is bijna altijd beter om expliciet te parametriseren verklaringen, in plaats van te vertrouwen op de server om het te doen. Door expliciet te zijn, heb je volledige controle over alle aspecten van het parametreringsproces, inclusief waar parameters worden gebruikt, de precieze gegevenstypen die worden gebruikt en wanneer plannen worden hergebruikt.

De meeste clients en stuurprogramma's bieden specifieke manieren om expliciete parametrering te gebruiken. Er zijn ook opties zoals sp_executesql , opgeslagen procedures en functies.

Ik ga niet in op de gerelateerde problemen van het snuiven van parameters of SQL-injectie, omdat ze, hoewel belangrijk, niet de focus van deze serie zijn. Toch moet je code schrijven met beide dichtbij je hoofd.

Voor legacy-applicaties of andere code van derden die niet eenvoudig kan worden gewijzigd, is expliciete parametrering mogelijk niet altijd mogelijk. U kunt mogelijk enkele obstakels overwinnen met behulp van sjabloonplanhandleidingen. In ieder geval zou het een ongebruikelijke werklast zijn die niet op zijn minst enkele geparametriseerde instructies aan de serverzijde bevat.

Shell-abonnementen

Toen SQL Server 2005 Geforceerde parametrering introduceerde , de bestaande auto-parameterisatie functie is hernoemd naar Eenvoudige parametrering . Ondanks de verandering in terminologie, eenvoudige parametrering werkt hetzelfde als auto-parameterisatie altijd gedaan:SQL Server probeert constante letterlijke waarden in ad-hocinstructies te vervangen door parametermarkeringen. Het doel is om compilaties te verminderen door het hergebruik van plannen in de cache te vergroten.

Laten we een voorbeeld bekijken, waarbij we de Stack Overflow 2010-database gebruiken op SQL Server 2019 CU 14. Databasecompatibiliteit is ingesteld op 150 en de kostendrempel voor parallellisme is ingesteld op 50 om parallellisme voorlopig te vermijden:

EXECUTE sys.sp_configure
    @configname = 'show advanced options',
    @configvalue = 1;
RECONFIGURE;
GO
EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 50;
RECONFIGURE;

Voorbeeldcode:

-- 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

Die uitspraken bevatten predikaten die alleen verschillen in hun constante letterlijke waarden. SQL Server past met succes eenvoudige parametrering toe , resulterend in een geparametriseerd plan. Het plan met enkele parameter wordt vier keer gebruikt, zoals we kunnen zien door de plancache op te vragen:

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;

De resultaten tonen een Adhoc plan cache-invoer voor elk origineel statement en een enkele Prepared plan:

Vier ad-hocplannen en één voorbereid plan

Een Voorbereid instructie is vergelijkbaar met een opgeslagen procedure, met parameters afgeleid van letterlijke waarden gevonden in de Adhoc uitspraak. Ik noem dit een nuttig mentaal model bij het nadenken over het parameterisatieproces aan de serverzijde.

Merk op dat SQL Server beide in de cache plaatst de originele tekst en de geparametriseerde vorm. Wanneer eenvoudige parametrering succesvol is, is het plan dat aan de originele tekst is gekoppeld Adhoc en bevat geen volledig uitvoeringsplan. In plaats daarvan is het in de cache opgeslagen plan een shell met heel weinig behalve een verwijzing naar de Prepared geparametriseerd plan.

De XML-weergave van de schaalplannen tekst bevatten zoals:

<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>

Dat is het hele plan. De ParameterizedPlanHandle punten van de Adhoc shell naar het volledige geparametriseerde plan. De handle-waarde is hetzelfde voor alle vier de schaalplannen.

Planstubs

Shell-plannen zijn kleiner dan een volledig samengesteld plan:16 KB in plaats van 40 KB in het voorbeeld. Dit kan nog steeds een aanzienlijke hoeveelheid geheugen opleveren als u veel instructies hebt met eenvoudige parametrering of veel verschillende parameterwaarden. De meeste SQL Server-instanties zijn niet zo overspoeld met geheugen dat ze het zich kunnen veroorloven om het op deze manier te verspillen. De shell-plannen worden door SQL Server als zeer bruikbaar beschouwd, maar het vinden en verwijderen ervan verbruikt bronnen en kan een twistpunt worden.

We kunnen het totale geheugenverbruik voor shell-abonnementen verminderen door de optie Optimaliseren voor ad-hocworkloads in te schakelen.

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;

Hiermee wordt een kleine stub in de cache opgeslagen wanneer voor het eerst een ad-hocstatement wordt aangetroffen in plaats van een shell. De stub dient als bladwijzer zodat de server kan onthouden dat hij de exacte instructietekst eerder heeft gezien. Wanneer dezelfde tekst een tweede keer wordt aangetroffen, gaat het compileren en cachen verder alsof optimaliseren voor ad-hocworkloads waren niet ingeschakeld.

Het voorbeeld opnieuw uitvoeren met optimize for ad hoc workloads ingeschakeld toont het effect op de plancache.

Samengestelde planstubs

Er wordt geen plan in de cache opgeslagen voor de ad-hocverklaringen, alleen een stub. Er is geen ParameterizedPlanHandle aanwijzer naar de Voorbereid plan, hoewel een volledig geparametriseerd plan is in de cache.

Het voor de tweede keer uitvoeren van de testbatches (zonder de plancache te wissen) geeft hetzelfde resultaat als bij optimalisatie voor ad-hocworkloads was niet ingeschakeld—vier Adhoc shell-plannen die wijzen naar de Prepared plannen.

Voordat u doorgaat, stelt u de optimalisatie voor ad-hocworkloads opnieuw in instelling op nul:

EXECUTE sys.sp_configure
    @configname = 'optimize for ad hoc workloads',
    @configvalue = 0;
RECONFIGURE;

Groottelimieten van plancache

Of er nu planshells of planstubs worden gebruikt, er zijn nog steeds nadelen aan al deze Adhoc cache-items. Ik heb al het totale geheugengebruik genoemd, maar elke plancache heeft ook een maximum aantal van inzendingen. Zelfs als het totale geheugengebruik geen probleem is, kan de enorme hoeveelheid dat wel zijn.

De limieten kunnen worden verhoogd met gedocumenteerde traceringsvlag 174 (aantal vermeldingen) en traceringsvlag 8032 (totale grootte). Afhankelijk van de werkbelasting en andere geheugenvereisten is dit misschien niet de beste oplossing. Het betekent tenslotte alleen maar meer laagwaardige Adhoc plannen, geheugen wegnemen van andere behoeften.

Alleen voorbereide plannen in cache plaatsen

Als de werklast zelden ad-hoc batches uitgeeft met precies dezelfde instructietekst, caching van planshells of planstubs is een verspilling van middelen. Het verbruikt geheugen en kan conflicten veroorzaken wanneer de SQL-abonnementen cacheopslag (CACHESTORE_SQLCP ) moet worden verkleind om binnen de geconfigureerde limieten te passen.

Het ideaal zou zijn om binnenkomende ad-hoc batches te parametriseren, maar alleen cache de geparametriseerde versie. Hieraan zijn kosten verbonden, omdat toekomstige ad-hoc-instructies moeten worden geparametriseerd voordat ze kunnen worden gekoppeld aan het geparametriseerde cacheplan. Aan de andere kant zou dit hoe dan ook zijn gebeurd, aangezien we al exact hebben vermeld tekstuele overeenkomsten zijn zeldzaam voor de doelbelasting.

Voor workloads die profiteren van eenvoudige parametrering, maar niet van de caching van Adhoc items, er zijn een aantal opties.

Ongedocumenteerde traceervlag

De eerste optie is om ongedocumenteerde traceervlag 253 in te schakelen. Dit voorkomt de caching van Adhoc plannen volledig. Het beperkt niet alleen het aantal van dergelijke plannen, of voorkomt dat ze in de cache 'blijven', zoals soms is gesuggereerd.

Traceringsvlag 253 kan worden ingeschakeld op sessieniveau - waarbij de effecten worden beperkt tot alleen die verbinding - of breder als een globale of opstartvlag. Het functioneert ook als een query-hint, maar het gebruik ervan voorkomt eenvoudige parametrering, wat hier contraproductief zou zijn. Er is een gedeeltelijke lijst van de dingen die eenvoudige parametrering in de weg staan ​​in de Microsoft Technical Paper, Plan Caching and Recompilation in SQL Server 2012.

Met traceringsvlag 253 actief voordat de batch wordt gecompileerd , alleen de Voorbereid uitspraken worden in de cache opgeslagen:

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

De plancachequery bevestigt alleen de Prepared statement wordt in de cache opgeslagen en opnieuw gebruikt.

Alleen de voorbereide instructie wordt in de cache opgeslagen

De niet-cacheable-batch

De tweede optie is om een ​​instructie op te nemen die de hele batch markeert als niet te cachen . Geschikte verklaringen zijn vaak veiligheidsgerelateerd of anderszins gevoelig op de een of andere manier.

Dit klinkt misschien onpraktisch, maar er zijn een paar oplossingen. Ten eerste hoeft de gevoelige instructie niet te worden uitgevoerd - deze hoeft alleen aanwezig . te zijn . Wanneer aan die voorwaarde is voldaan, heeft de gebruiker die de batch uitvoert niet eens toestemming nodig om de gevoelige instructie uit te voeren. Let op:het effect is beperkt tot de batch die de gevoelige verklaring bevat.

Twee voldoende gevoelige uitspraken en voorbeeldgebruik worden hieronder weergegeven (met de testverklaringen nu in een enkele 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

De Voorbereid plannen gemaakt door eenvoudige parametrering worden nog steeds in de cache opgeslagen en opnieuw gebruikt, ondanks dat de bovenliggende batch is gemarkeerd als niet-cachebaar.

Alleen de voorbereide instructie wordt in de cache opgeslagen

Geen van beide oplossingen is ideaal, maar totdat Microsoft een gedocumenteerde en ondersteunde oplossing voor dit probleem biedt, zijn dit de beste opties die ik ken.

Einde van deel 1

Over dit onderwerp valt nog veel meer te vertellen. Deel twee behandelt de datatypes die worden toegewezen bij eenvoudige parametrering is in dienst.


  1. postgresql - sql - aantal `echte` waarden

  2. Hoe de Oracle-database te controleren op langlopende query's

  3. Aangepaste volgorde in Oracle SQL

  4. Een 64-bits applicatie verbinden met Acomba