sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server Internals:Plan Caching Pt. II – Sammanställning av planer

Detta är en del av en SQL Server Internals Plan Caching-serie. Se till att läsa Kalens första inlägg om detta ämne.

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

I min föregående artikel , jag pratade om SQL-serverdiagnostik, inklusive de olika alternativen som SQL Server har för att återanvända en frågeplan. Vi tittade på tre typer av frågeplaner:adhoc, förberedda och procedur. Jag avslutade diskussionen med en titt på en olämplig återanvändning av en plan, som kan hända när SQL Server tillämpar parametersniffning i fel situationer. Om en plan är baserad på ett initialt värde som gör att optimeraren genererar en plan som är lämplig för det värdet och sedan samma plan används för ett annat värde, kanske planen inte längre är optimal.

Så vad kan vi göra när parametersniffning är ett problem? Vi kan tvinga SQL Server att komma med en ny plan. Vanligtvis kallar vi handlingen att komma med en ny plan för "omkompilering", men det borde förmodligen kallas "omoptimering". Men de flesta använder termen "kompilera om", så det är vad jag kommer att använda här.

Om olämplig användning av parametersniffning är ett problem är en enkel lösning att bara berätta för SQL Server att komma med en ny plan. För individuella uttalanden, till exempel med PREPARED planer som har autoparameteriserats, kan vi lägga till RECOMPILE-tipset till en fråga. Med hjälp av FORCED parametriserad (diskuterad i föregående artikel), kommer denna fråga att parametriseras.

SELECT * FROM dbo.newsales 
WHERE SalesOrderID < @num;

Om vi ​​vill se till att vi får en ny plan varje gång vi kör den här frågan, med potentiellt mycket olika värden för @num, kan vi lägga till RECOMPILE-tipset som visas:

SELECT * FROM dbo.newsales
  WHERE SalesOrderID < @num
OPTION (RECOMPILE);

För lagrade procedurer har vi tre alternativ. Först kan vi ta reda på om omkompilering verkligen kommer att hjälpa prestanda genom att utföra proceduren med alternativet RECOMPILE:

EXEC get_sales_range 66666 WITH RECOMPILE;

Det här alternativet kommer att göra att en ny plan genereras bara för den här exekveringen. Det kommer inte att sparas och kommer definitivt inte att återanvändas. Usecount-värdet som visas i sp_cacheobjects (beskrivs i föregående inlägg) för proceduren kommer inte att öka eftersom den ursprungliga planen inte återanvänds.

För det andra, om vi upptäcker att det hjälper att köra MED RECOMPILE, kan vi överväga att återskapa proceduren med RECOMPILE-alternativet, i vilket fall den aldrig kommer att återanvända planen och proceduren kommer inte att visas i planens cache alls.

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

För min enkla lilla procedur kan det vara meningsfullt att använda alternativet MED RECOMPILE för hela proceduren. Men om proceduren är mer komplex kanske det inte är meningsfullt att kompilera om hela proceduren eftersom ett påstående orsakar problem. Så, det tredje alternativet är att använda RECOMPILE-tipset för ett uttalande i proceduren, så det ser ut så här:

DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num
    OPTION (RECOMPILE);
GO

Att använda ett av dessa RECOMPILE-alternativ kan tvinga SQL Server att komma med en ny plan på din begäran. Nu ska vi titta på när din SQL Server-diagnostik kommer med en ny plan när du inte begär det, dvs. när sker automatisk omkompilering av en befintlig plan?


Automatisk omkompilering av en plan sker i två typer av situationer:

  • För det första, om optimeraren avgör att den befintliga planen inte längre är korrekt, vanligtvis på grund av en förändring i objektdefinitionerna, måste den komma med en ny plan. Till exempel, om du har en plan för en fråga som väljer från TabellA, och du sedan släpper flera kolumner, eller ändrar datatyper för kolumner i TabellA, kommer SQL Server att kompilera om frågan för att komma fram till en plan som återspeglar DDL-ändringarna.
  • Den andra situationen där automatisk omkompilering inträffar är när SQL Server bestämmer att planen kanske inte längre är optimal, på grund av en förändring i statistiken. I de flesta fall, om statistik på någon av kolumnerna eller indexen har uppdaterats sedan senast planen sammanställdes, kommer den att kompileras om. Men detta leder till en annan fråga. När uppdateras statistiken? Statistiken kan uppdateras automatiskt när tillräckligt många rader i de relevanta kolumnerna har ändrats. Hur många räcker? Vi pratar om det snart.

Som standard kommer SQL Server att uppdatera statistiken automatiskt på grund av ett databasalternativ som är PÅ som standard. Men om du är en databasägare (eller en SQL 'sa', som visas som ägare i varje databas), kan du ändra alternativen. Ett av alternativen heter AUTO_UPDATE_STATISTICS och ett annat heter AUTO_UPDATE_STATISTICS_ASYNC. Alternativet AUTO_UPDATE_STATISTICS är PÅ i tempdb-databasen, så varje ny databas ärver detta alternativ. När det här alternativet är PÅ och sökmotorn upptäcker ändringar av ett tillräckligt antal rader medan en fråga bearbetas, kommer exekveringen att pausas medan statistiken uppdateras och sedan kompileras frågan om. Det andra alternativet, AUTO_UPDATE_STATISTICS_ASYNC, kan potentiellt ha mindre effekt på exekveringstiden för frågor eftersom exekveringen inte pausar, till priset av att använda en eventuell suboptimal plan. Med det andra alternativet, om exekveringsmotorn upptäcker ett behov av att uppdatera statistik, avfyras en bakgrundstråd för att göra uppdateringen, och huvudtråden fortsätter att köra frågan med den ursprungliga statistiken och den ursprungliga planen. Nästa fråga som kommer åt de berörda tabellerna och ser den uppdaterade statistiken kommer att kompilera om frågan, men den kommer inte att pausa och uppdatera statistiken mitt under körningen.

Det finns några fler situationer samt några frågetips som styr huruvida planer kompileras om eller inte, så jag ska visa dig ett flödesschema. Jag delar ett flödesschema med dig som jag skapade för mina träningsklasser på SQL Server-interna komponenter.

Pilen är där SQL Server börjar bearbeta din batch. Den kontrollerar först om det redan finns en plan för din batch i cachen, och om svaret är NEJ följer du pilen till höger och sammanställer en plan. Planen läggs i cache och sedan startar SQL Server igen. Ja, planen ska finnas i cachen den här gången, så då följer den pilen nedåt och frågar om en ledtråd som heter KEEP PLAN har använts. Om JA, börjar SQL Server exekvera planen omedelbart och gör inga ytterligare kontroller.

Nästa fråga är om några DDL-ändringar har gjorts. Om nej, frågar den om flera andra situationer som jag inte kommer att kunna prata om i den här artikeln. Faktum är att jag verkligen inte kommer att gå igenom alla alternativ här. Jag lämnar det till dig. Men om du har några frågor eller förvirringar, ställ dem gärna i kommentarsfältet här, eller tweeta mig på @sqlqueen. Jag kommer att peka på frågan längst till höger:Är AUTO_STATS_ASYNC PÅ? Här kan du se att om svaret är JA så finns det två åtgärder. En gren börjar precis utföras med den befintliga planen, och den andra är bakgrundstråden som uppdaterar statistiken men som sedan inte gör något annat. Nästa fråga kommer att stöta på beslutsrutan i mitten "Är ny statistik tillgänglig" och bör svara JA, så frågan kommer att kompileras om.

Det enda andra jag kommer att prata om är frågan "Är någon statistik inaktuell?" Detta betyder i princip att statistiken är inaktuell eftersom för många ändringar har gjorts. Så nu kan vi prata om hur många som är för många.

Även om det finns olika värden som används för mycket små tabeller, för alla tabeller med mer än 500 rader i den, före SQL Server 2016, skulle statistik anses vara "inaktuell" när antalet ändringar i kolumnen som statistiken baserades översteg 20 % av antalet rader i tabellen. Så för en tabell med 1 000 rader kan detta betyda 200 infogningar, 200 uppdateringar eller 200 borttagningar. Det kan vara 200 radändringar eller 5 rader uppdaterade 40 gånger vardera. SQL Server ger oss till och med en funktion som rapporterar hur många ändringar som har gjorts. Du måste leta upp stats_id-numret för statistiken du är intresserad av, vilket skulle vara index_id om statistiken tillhör ett index. Stats_id kan hittas i vyn som heter sys.stats. I min nyhetstabell använder jag den här frågan för att se att stats_id för indexet i kolumnen SubTotal är 3.

SELECT name, stats_id FROM sys.stats
WHERE object_id = object_id('newsales');

Sedan kan jag använda det värdet för att titta på antalet ändringar. Låt mig uppdatera några rader först:

UPDATE newsales
SET SubTotal = SubTotal * 0.9
WHERE SalesOrderID < 45200
(1541 rader påverkade)

SELECT * FROM sys.dm_db_stats_properties(object_id('newsales'), 3);  

Faktum är att 20% är ett STORT tal. Och för många tabeller kan frågor dra nytta av uppdaterad statistik med mycket färre än 20 % av raderna uppdaterade. Från och med 2008R2 SP1 inkluderade SQL Server en Traceflag som du kunde använda för att ändra antalet rader till en glidande skala, som visas i följande graf:

Från och med SQL Server 2016 används den här nya algoritmen med glidande skala som standard, så länge du är på kompatibilitetsnivå 130 eller högre.

De flesta automatiska omkompileringar av frågeplaner beror på förändringar i statistiken. Men som jag nämnde ovan är det inte den enda anledningen till en omkompilering. Men eftersom det är det vanligaste kan det vara mycket användbart att vara medveten om när och hur statistik uppdateras och se till att statistiken på dina kritiska tabeller uppdateras tillräckligt ofta för att du ska få de bästa planerna!

Analysera prestandadata automatiskt för att utföra SQL-serverdiagnostik för att snabbt lösa problem och identifiera servrar där prestandaförsämring har sitt ursprung. Kom igång med Spotlight Cloud idag:


  1. Tabeller och index jämfört med hårddisk och SSD

  2. MySQL-anslutning över SSH-tunnel - hur specificerar man annan MySQL-server?

  3. uppdateringsfråga med join på två tabeller

  4. DATABASE() – Hämta det aktuella databasnamnet i MySQL