sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server Internals:Plan Caching Pt. I – Återanvändning av planer

SQL Server har funnits i över 30 år, och jag har arbetat med SQL Server nästan lika länge. Jag har sett många förändringar under åren (och decennier!) och versioner av denna otroliga produkt. I dessa inlägg kommer jag att dela med mig av hur jag ser på några av funktionerna eller aspekterna av SQL Server, ibland tillsammans med lite historiskt perspektiv.

Kolla in Kalens senaste bloggar om problematiska operatörer här.

Planer för SQL-serverdiagnostik kan vara dyra att skapa, eftersom frågeoptimeraren måste kunna hitta en bra plan för alla juridiska frågor som skickas. Optimizern utvärderar flera sammanfogningsorder, flera index. och olika typer av sammanfognings- och grupperingsalgoritmer, beroende på din fråga och vilka tabeller som är involverade. Om samma fråga körs igen kan SQL Server spara mycket resurser genom att återanvända en befintlig plan. Men det är inte alltid möjligt att återanvända en befintlig plan, och det är inte alltid bra att göra det. I de följande två artiklarna kommer vi att titta på när en plan återanvänds och när den kompileras om.

Först ska vi titta på de olika varianterna av planer och metadatavyn som jag använder oftast för att titta på vad som finns i min plancache. Jag har skrivit en egen vy som ger den information som jag finner mest användbar oftast. SQL Server cachar sex olika typer av frågeplaner, men endast två används normalt för plancachejustering. Dessa är COMPILED PLAN och COMPILED PLAN STUB. Min vy filtrerar bort alla utom dessa två typer av cacheobjekt. SAMMANSTÄLLDA PLANER finns i tre varianter:AD HOC, PREPARED och PROC. Jag kommer att kommentera alla tre typerna.

Även om vi bara tittar på KOMPILERADE PLANER, finns det fortfarande många planer i cachen som vanligtvis måste ignoreras, eftersom de genereras av SQL Server själv. Dessa inkluderar planer som letar efter filströms- eller fulltextsökningsindex eller interna frågor som arbetar med In-memory OLTP. Så, min vy lägger till filter för att försöka avvänja de flesta av de planer jag inte är intresserad av. Du kan ladda ner ett skript för att bygga denna vy, kallat sp_cacheobjects , från här.

Även med alla filter som min vy använder, finns det fortfarande några av SQL Servers egna interna frågor i cachen; Jag brukar rensa plancachen ofta när jag testar i det här området. Det enklaste sättet att rensa ALLA planer från cachen är med kommandot:DBCC FREEPROCCACHE.

Adhoc-kompilerade planer

Den enklaste typen av plan är Adhoc. Detta används för grundläggande frågor som inte passar in i en annan kategori. Om du har laddat ner mitt skript och skapat min sp_cacheobjects-vy kan du köra följande. Alla versioner av AdventureWorks-databasen borde fungera. Detta skript gör en kopia av en tabell och bygger ett par unika index på den. Den masserar också SubTotal-beloppet för att ta bort eventuella decimalsiffror.

USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS newsales;
GO
-- Make a copy of the Sales.SalesOrderHeader table
SELECT * INTO dbo.newsales
FROM Sales.SalesOrderHeader;
GO
UPDATE dbo.newsales
SET SubTotal = cast(cast(SubTotal as int) as money);
GO
CREATE UNIQUE index newsales_ident
    ON newsales(SalesOrderID);
GO
CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
GO
-- Adhoc query plan reuse
DBCC FREEPROCCACHE;
GO
-- adhoc query
SELECT * FROM dbo.newsales
WHERE SubTotal = 4;
GO
SELECT * FROM sp_cacheobjects;
GO

I min produktion ser du två Adhoc-planer. En är för SELECT-satsen från nyhetsreklamen tabellen, och den andra är för SELECT från mina sp_cacheobjects se. Eftersom planen är cachad, om EXAKT samma fråga körs igen, kan samma plan återanvändas och du kommer att se användningsantal värdeökning. Det finns dock en hake. För att en Adhoc-plan ska kunna återanvändas måste SQL-strängen vara helt exakt densamma. Om du ändrar några tecken i SQL, känns inte frågan igen som samma fråga, och en ny plan genereras. Om jag till och med lägger till ett blanksteg, inkluderar kommentaren eller en ny radbrytning, är det inte samma sträng. Om jag ändrar skiftläge betyder det att det finns olika ASCII-kodvärden, alltså inte samma sträng.

Du kan prova detta själv genom att köra olika varianter av mitt första SELECT-uttalande från nyhetsreklamen tabell. Du kommer att se en annan rad i cachen för var och en. Efter att jag kört flera varianter – ändra numret jag sökte efter, ändra skiftläge, lägga till kommentaren och en ny rad, ser jag följande i cachen. SELECT från min vy återanvänds, men allt annat har usecounts värde på 1.

Ett ytterligare krav för återanvändning av Adhoc-frågeplan är att sessionen som kör frågan måste ha samma SET-alternativ i kraft . Det finns en annan kolumn i utgången som du kan se till höger om frågetexten, kallad SETOPTS. Detta är en bitsträng med en bit för varje relevant SET-alternativ. Om du ändrar ett av alternativen, till exempel, SET ANSI_NULLS OFF, kommer bitsträngen att ändras och samma plan med den ursprungliga bitsträngen kan inte återanvändas.

Förberedda sammanställda planer

Den andra typen av cachad kompilerad plan är en FÖRBEREDAD plan. Om din fråga uppfyller en viss uppsättning krav. Det kan faktiskt parametreras automatiskt. Det visas i metadatan som FÖRBEREDT och SQL-strängen visar en parametermarkör. Här är ett exempel:

PREPARED-planen visar parametermarkören som @1 och inkluderar inte det faktiska värdet. Observera att det finns en rad för en ADHOC-fråga med ett verkligt värde på 5555, men det är faktiskt bara ett "skal" av den verkliga frågan. Den cachelagrar inte hela planen utan bara frågan och några få identifierande detaljer, för att hjälpa frågeprocessorn att hitta den parametriserade planen i cachen. Lägg märke till storleken (sidaanvänd ) är mycket mindre än den FÖRBEREDDA planen.

Standardparametreringsläget, kallat SIMPLE parameterization, är extremt strikt vad gäller vilka planer som kan parametreras. Det är egentligen bara den enklaste av frågorna som kan parametreras som standard. Frågor som innehåller JOIN, GROUP BY, OR och många andra relativt vanliga frågekonstruktioner förhindrar att en fråga parametreras. Förutom att inte ha någon av dessa konstruktioner, är det viktigaste för ENKEL parameterisering att frågan är SÄKER. Det betyder att det bara finns en möjlig plan oavsett vilka värden som skickas för några parametrar. (Naturligtvis kan en fråga utan några parametrar också vara SÄKER.) Min fråga söker efter en exakt matchning i kolumnen SalesOrderID , som har ett unikt index på sig. Så det befintliga icke-klustrade indexet kan användas för att hitta valfri matchande rad. Oavsett vilket värde jag använder, 55555 eller något annat, kommer det aldrig att finnas mer än en rad vilket betyder att planen fortfarande kommer att vara bra.

I mitt exempel på Adhoc-frågeplan letade jag efter matchande värden för SubTotal . Vissa SubTotal värden förekommer några gånger eller inte alls, så ett icke-klustrat index skulle vara bra. Men andra värden kan förekomma många gånger, så indexet skulle INTE vara användbart. Därför är frågeplanen inte SÄKER och frågan kan inte parametriseras. Det är därför vi såg en Adhoc-plan för mitt första exempel.

OM du har frågor med JOIN eller andra otillåtna konstruktioner, kan du tala om för SQL Server att vara mer aggressiv i parametrering genom att ändra ett databasalternativ:

ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
GO

Att ställa in din databas till FORCED parameterization innebär att SQL Server kommer att parametrera en hel del fler frågor, inklusive de med JOIN, GROUP BY, OR, etc. Men det betyder också att SQL Server kan parametrisera en fråga som inte är SÄKER. Den kan komma med en plan som är bra när bara ett fåtal rader returneras, och sedan återanvända planen när många rader returneras. Detta kan sluta med mycket suboptimal prestanda.

Ett sista alternativ för en utarbetad plan är när du uttryckligen förbereder en plan. Detta beteende anropas vanligtvis genom ett program med SQLPrepare och SQLEexe API:er. Du anger vad som är frågan med parametermarkeringar, du anger datatyperna och du anger de specifika värden som ska användas. Samma fråga kan sedan köras igen med olika specifika värden och den befintliga planen kommer att användas. Även om det kan vara möjligt att använda explicit förberedda planer för de fall där SQL Server inte parametrerar och du önskar att det skulle göra det, hindrar det inte SQL Server från att använda en plan som INTE är bra för efterföljande parametrar. Du måste testa dina frågor med många olika ingångsvärden och se till att du får förväntad prestanda om och när en plan återanvänds.

Metadata (t.ex. mina sp_cacheobjects visa) visar bara FÖRBEREDAD för alla tre typer av planer:FORCERAD och ENKEL autoparameterisering och EXPLICIT parameterisering.

Proc-kompilerade planer

Den sista objtypen värde för sammanställda planer är för en lagrad procedur, som visas som Proc. När det är möjligt är lagrade procedurer det bästa valet för återanvändbar kod, på grund av deras enkla hantering från själva servern, men det betyder inte att de garanterat alltid ger den bästa prestandan. Precis som att använda FORCED-parameteriseringsalternativet (och även den explicita parameteriseringen), använder lagrade procedurer "parametersniffning". Detta betyder att det första parametervärdet som skickas in bestämmer planen. Om efterföljande körningar fungerar bra med samma plan, är parametersniffning inte ett problem och kan faktiskt vara fördelaktigt eftersom det sparar oss kostnaden för att kompilera om och omoptimera. Men om efterföljande avrättningar med olika värden inte skulle använda den ursprungliga planen, så har vi problem. Jag ska visa dig ett exempel på parametersniffning som orsakar problem

Jag skapar en lagrad procedur baserat på nyhetsförsäljningen tabell vi använde tidigare. Proceduren kommer att ha en enda fråga, som filtrerar baserat på SalesOrderID kolumn, på vilken vi byggde ett icke-klustrat index. Frågan kommer att baseras på en olikhet, så för vissa värden kan frågan returnera bara några rader och använda indexet, och för andra värden kan frågan returnera MÅNGA rader. Med andra ord, frågan är inte SÄKER.

USE AdventureWorks2016;
GO
DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO

Jag använder alternativet SET STATISTICS IO ON för att se hur mycket arbete som görs när proceduren exekveras. Först kör jag den med en parameter som bara returnerar några få rader:

SET STATISTICS IO ON
GO
EXEC get_sales_range 43700;
GO

STATISTICS IO-värdet rapporterar att det tog 43 logiska läsningar för att returnera 41 rader. Detta är normalt för ett icke-klustrat index. Nu kör vi proceduren igen med ett mycket större värde.

EXEC get_sales_range 66666;
GO
SELECT * FROM sp_cacheobjects;
GO
This time, we see that SQL Server used a whole lot more reads:

Faktum är att en tabellskanning på nyhetsreklam tabell tar bara 843 läsningar, så detta är mycket sämre prestanda än en tabellskanning. sp_cacheobjects vyn visar oss att PROC-planen har återanvänts för denna andra körning. Detta är ett exempel på när parametersniffning INTE är bra.

Så vad kan vi göra när parametersniffning är ett problem? I nästa inlägg kommer jag att berätta för dig när SQL Server kommer med en ny plan och inte återanvänder gamla. Vi kommer att titta på hur du kan tvinga fram (eller uppmuntra) omkompilering, och vi kommer också att se när SQL Server automatiskt omkompilerar dina frågor.

Spotlight Cloud kan revolutionera din prestandaövervakning och SQL-serverdiagnostik. Kom igång med din kostnadsfria provperiod genom att använda länken nedan:


  1. Migrera från AnswerHub till WordPress :A Tale of 10 Technologies

  2. MariaDB ROUND() vs TRUNCATE()

  3. Applikationsanvändare kontra Row Level Security

  4. Oracles återgång till användning i Java (JDBC, Prepared Statement)