sql >> Databasteknik >  >> RDS >> Database

Fånga exekveringsplanvarningar med utökade händelser

Vi undervisar i IEPTO2 i Dublin den här veckan (och om Irland inte finns på din lista över platser att se under denna livstid måste du lägga till det... det är fantastiskt här) och idag avslutade jag modulen Query Plan Analysis. En sak jag tar upp är intressanta saker du kan hitta i frågeplanen, till exempel:

  • NoJoinPredicate (2005 och högre)
  • ColumnsWithNoStatistics (2005 och högre)
  • UnmatchedIndex (2008 och högre)
  • PlanAffectingConvert (2012 och senare)

Dessa attribut är bra att leta efter när du tittar på en enstaka plan, eller uppsättning planer, medan du ställer in. Men om du vill vara lite mer proaktiv kan du börja bryta plancachen och leta efter dem där. Att göra det kräver naturligtvis att du skriver lite XQuery, eftersom planerna är XML (för detaljer om showplan-schemat, kolla in:http://schemas.microsoft.com/sqlserver/2004/07/showplan/). Jag älskar inte XML, men inte i brist på försök, och när en av deltagarna frågade om du kunde fånga frågor som hade attributet NoJoinPredicate genom Extended Events, tänkte jag:"Vilken bra idé, jag måste kolla upp .”

Visst, det finns ett evenemang för det. Det finns ett evenemang för alla fyra av de jag listade ovan:

  • missing_join_predicate
  • missing_column_statistics
  • unmatched_filtered_indexes
  • plan_påverkar_konvertera

Trevlig. Att ställa in dessa i en Extended Event-session är ganska enkelt. I det här fallet skulle jag rekommendera att använda målet event_file, eftersom du förmodligen kommer att starta händelsesessionen och låta den köra ett tag innan du går tillbaka och granskar utdata. Jag har inkluderat några åtgärder, i hopp om att dessa händelser inte avfyras att ofta, så vi lägger inte till för mycket omkostnader här. Jag inkluderade sql_text även om det inte är en åtgärd som du verkligen bör lita på. Jonathan har diskuterat detta tidigare, men sql_text ger dig bara inputbuffer, så du kanske inte får hela historien för frågan. Av den anledningen inkluderade jag även plan_handle. Förbehållet är att, beroende på när du letar efter planen, kanske den inte längre finns i planens cache.

-- Ta bort händelsesession om den existerar OM FINNS (VÄLJ 1 FRÅN [sys].[server_event_sessions]WHERE [name] ='InterestingPlanEvents')BÖRJA SLÄPP EVENT SESSION [InterestingPlanEvents] PÅ SERVERENDGO -- Definiera händelsesessionCREATE [Interesting EVENT SESSIONE ]ON SERVERADD EVENT sqlserver.missing_column_statistics( ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text) WHERE ([paket0].[equal_boolean]([sqlserver].[är_system],[sql_server] OCH. database_id]>(4))),ADD EVENT sqlserver.missing_join_predicate( ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text) WHERE ([sqlserver].[is_system]=(0) OCH [sqlserver].[database_id]>(4))),ADD EVENT sqlserver.plan_affecting_convert( ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text) WHERE ([paket0].[equal_boolean]([sqlserver].[is_system],(0)) OCH [sqlserver].[database_id]>(4))),ADD EVENT sqlserver.unmatched_filtered_indexes( ACTION(sqlserver.plan_handle,sqlserver.sql_text ) VAR ([package0].[equal_boolean]([sqlserver].[is_system],(0)) OCH [sqlserver].[database_id]>(4)))ADD TARGET package0.event_file( SET filnamn=N'C:\temp\InterestingPlanEvents' /* byt plats om det är lämpligt */)WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SEKUNDER,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=START_CANUSALITY_MODE,--MEMORY_PARTITION_MODE=Starta eventsessionenALTER EVENT SESSION [InterestingPlanEvents] PÅ SERVER STATE=START;GO

När vi har evenemangssessionen igång kan vi generera dessa händelser med exempelkoden nedan. Observera att den här koden förutsätter en nyinstallation av AdventureWorks2014. Om du inte har en, kanske du inte ser händelsen missing_column_statistics utlösas om du tillfrågas kolumnen [HireDate] i [HumanResources].[Employee].

-- Dessa frågor förutsätter en FÄRSK återställning av AdventureWorks2014ALTER DATABASE [AdventureWorks2014] STÄLL AV AUTO_CREATE_STATISTICS;GO USE [AdventureWorks2014];GO CREATE INDEX [NCI_SalesOrderHeader].[ON [SalesOrderHeader]. [TotalDue], [DueDate])WHERE [SubTotal] > 10000.00;GO /*No join predicateNOT:Vi rensar proceduren här eftersom händelsen ENDAST aktiveras för den *initiella* kompileringen*/DBCC FREEPROCCACHE; /* Ej för produktionsanvändning */ VÄLJ [h].[SalesOrderID], [d].[SalesOrderDetailID], [h].[Kund-ID]FRÅN [Sales].[SalesOrderDetail] [d],[Sales].[SalesOrderHeader ] [h]WHERE [d].[ProductID] =897;GO -- Kolumner utan statistikVÄLJ [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]FRÅN [HumanResources].[Anställd]WHERE [HireDate] >='2013-01-01';GO -- Unmatched IndexDECLARE @Total MONEY =10000,00; VÄLJ [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]FRÅN [Sales].[SalesOrderHeader]WHERE [SubTotal] > @Total;GO -- Plan som påverkar ConvertSELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]FRÅN [HumanResources].[Anställd]WHERE [NationalIDNumber] =345106466;GO ALTER EVENT] ON SERVERSTATE=STOP;GO SLIP EVENT SESSION [InterestingPlanEvents]PÅ SERVER;GO

OBS:EFTER att du har hämtat planer från cachen kan du köra ALTER-satsen för att aktivera alternativet skapa statistik automatiskt. Om du gör det vid denna tidpunkt rensas plancachen och du måste börja om från början med din testning. (Och vänta tills du är klar med att ta bort indexet.)

ÄNDRA DATABAS [AdventureWorks2014] SÄTT AUTO_CREATE_STATISTICS PÅ;GO DROP INDEX [NCI_SalesOrderHeader] PÅ [Sales].[SalesOrderHeader];GO

Eftersom jag har stoppat händelsesessionen öppnar jag utdatafilen i SSMS för att se vad vi fångade:

Utdata från utökade händelser

För vår första fråga med ett saknat join-predikat har vi en händelse som dyker upp, och jag kan se texten för frågan i sql_text-fältet. Men vad jag verkligen vill är att titta på planen också, så jag kan ta plan_handle och kolla sys.dm_exec_query_plan:

 SELECT QUERY_PLAN FRÅN SYS.DM_EXEC_QUERY_PLAN (0x06000700E2200333405DD12C000000000100000000000000000000000000000000000000000000000000000000000000000000000000S 

Och öppna det i SQL Sentry Plan Explorer:

Att gå med predikat saknas

Planen har en visuell indikator på det saknade join-predikatet i den kapslade slingan (det röda X), och om jag håller muspekaren över det ser jag varningen (och den finns i XML för planen). Utmärkt...Jag kan nu gå och prata med mina utvecklare om att skriva om den här frågan.

Nästa händelse är för en saknad kolumnstatistik. Jag tvingade helt fram den här situationen genom att stänga av AUTO_CREATE_STATISTICS för AdventureWorks2014-databasen. Jag rekommenderar inte detta på något sätt, form eller form. Det här alternativet är aktiverat som standard och jag rekommenderar att du alltid låter det vara aktiverat. Att stänga av den är dock det enklaste sättet att skapa denna händelse. Jag har återigen frågan i sql_text-fältet, men jag kommer att använda plan_handle igen för att dra planen:

 SELECT QUERY_PLAN FRÅN SYS.DM_EXEC_QUERY_PLAN (0x0600070044448323810921C360000000001000000000000000000000000000000000000000000000000000000000000000000000000S 

Statistik saknas

Och vi har återigen en visuell signal (den gula triangeln med utropstecken) för att indikera att det finns ett problem med planen, och återigen finns det i XML. Härifrån skulle jag först kontrollera om AUTO_CREATE_STATISTICS är inaktiverat, och om inte, skulle jag börja köra frågan i Management Studio för att se om jag kan återskapa varningen (och tvinga statistiken att skapa).

Nu är de återstående händelserna lite mer intressanta.

Du kommer att märka att vi har tre unmatched_filtered_indexes-händelser. Jag har ännu inte bestämt varför, men jag jobbar på det och kommer att skriva i kommentarerna om/när jag får det ordnat. För nu räcker det att jag har händelsen, och inom händelsen kan vi också se objektinformation så att jag känner till indexet i fråga:

NCI_SalesOrderHeader-index refererat av saknad indexhändelse

Och jag kan återigen ta plan_handle för att hitta frågeplanen:

Omatchat index

Den här gången ser jag varningen i SELECT-operatorn, så jag vet att det är något jag behöver undersöka närmare. I det här fallet har du alternativ för att få optimeraren att använda det filtrerade indexet när du använder parametrar, och jag rekommenderar att du går igenom Aarons inlägg för mer information om hur du använder filtrerade index.

Slutligen har vi nio händelser för plan_affecting_convert. Vad i helvete? Jag håller fortfarande på att ta reda på den här, men jag använde alternativet Track Causality för min händelsesession (när jag testade) för att bekräfta att alla händelser är en del av samma uppgift (de är). Om du tittar på uttryckselementet i utgången ser du att det ändras något (liksom compile_time), och detta dyker upp när du tittar på detaljerna i varningen i SQL Sentrys Plan Explorer (se andra skärmbilden nedan). I händelseutgången gör det uttryckselementet berätta vilken kolumn det handlar om, vilket är en början men inte tillräckligt med information, så återigen måste vi gå och hämta planen:

 SELECT QUERY_PLAN FRÅN SYS.DM_EXEC_QUERY_PLAN (0x0600070023747010E09E1C36000000000100000000000000000000000000000000000000000000000000000000000000000000S 

Plan som påverkar konvertering

Konverteringsdetalj från planen

Vi ser återigen vår vän, den gula triangeln, i SELECT-operatorn, och inom XML kan vi hitta attributet PlanAffectingConvert. Det här attributet lades till i SQL Server 2012 showplan-schemat, så om du kör en tidigare version kommer du inte att se detta i planen. Att lösa denna varning kan kräva lite mer arbete - du måste förstå var du har en datatypsfel och varför, och sedan antingen börja modifiera koden eller schemat ... båda kan mötas av motstånd. Jonathan har ett inlägg som diskuterar implicit konvertering mer i detalj, vilket är ett bra ställe att börja om du inte har arbetat med konverteringsproblem tidigare.

Sammanfattning

Extended Events-biblioteket med evenemang fortsätter att växa, och en sak att tänka på vid felsökning i SQL Server är om du kan få informationen du söker på ett annat sätt. Kanske för att det är enklare (jag föredrar verkligen XE framför XML!), eller för att det är mer effektivt eller ger dig mer detaljer. Oavsett om du proaktivt letar efter frågeproblem i din miljö eller reagerar på ett problem som någon har rapporterat men du har problem med att hitta det, är utökade händelser ett lämpligt alternativ att överväga, särskilt eftersom fler nya funktioner läggs till i SQL Server.


  1. Långsam MySQL-starttid i GTID-läge? Storleken på binär loggfil kan vara problemet

  2. Kan PostgreSQL utföra en koppling mellan två lagrade SQL Server-procedurer?

  3. Skapa en webbapp från grunden med Python Flask och MySQL:Del 4

  4. Bulk infoga fält med fast bredd