Database
 sql >> Datenbank >  >> RDS >> Database

Erfassen Sie Ausführungsplanwarnungen mithilfe von erweiterten Ereignissen

Wir unterrichten IEPTO2 diese Woche in Dublin (und wenn Irland nicht auf Ihrer Liste der Orte steht, die Sie in diesem Leben sehen sollten, müssen Sie es hinzufügen … es ist fantastisch hier) und heute habe ich das Modul Abfrageplananalyse abgeschlossen. Eine Sache, die ich behandle, sind interessante Dinge, die Sie im Abfrageplan finden können, zum Beispiel:

  • NoJoinPredicate (2005 und höher)
  • ColumnsWithNoStatistics (2005 und höher)
  • UnmatchedIndexes (2008 und höher)
  • PlanAffectingConvert (2012 und höher)

Auf diese Attribute sollten Sie achten, wenn Sie sich beim Tuning einen einzelnen Plan oder eine Reihe von Plänen ansehen. Aber wenn Sie etwas proaktiver sein möchten, können Sie damit beginnen, den Plan-Cache zu minen und dort nach ihnen zu suchen. Natürlich erfordert dies das Schreiben von XQuery, da die Pläne XML sind (Einzelheiten zum Showplan-Schema finden Sie unter:http://schemas.microsoft.com/sqlserver/2004/07/showplan/). Ich mag XML nicht, obwohl ich es nicht versucht habe, und als einer der Teilnehmer fragte, ob Sie Abfragen mit dem NoJoinPredicate-Attribut über erweiterte Ereignisse erfassen könnten, dachte ich:„Was für eine großartige Idee, muss ich überprüfen .“

Sicher genug, dafür gibt es ein Event. Es gibt ein Ereignis für alle vier oben aufgelisteten:

  • missing_join_predicate
  • missing_column_statistics
  • unmatched_filtered_indexes
  • plan_affecting_convert

Hübsch. Diese in einer Sitzung für erweiterte Ereignisse einzurichten, ist ziemlich einfach. In diesem Fall würde ich empfehlen, das Ziel event_file zu verwenden, da Sie wahrscheinlich die Ereignissitzung starten und eine Weile laufen lassen, bevor Sie zurückgehen und die Ausgabe überprüfen. Ich habe ein paar Aktionen eingefügt, in der Hoffnung, dass diese Ereignisse nicht das ausgelöst werden oft, also fügen wir hier nicht zu viel Overhead hinzu. Ich habe sql_text eingefügt, obwohl es keine Aktion ist, auf die Sie sich wirklich verlassen sollten. Jonathan hat dies bereits zuvor besprochen, aber sql_text gibt Ihnen nur den Eingabepuffer, sodass Sie möglicherweise nicht die vollständige Geschichte für die Abfrage erhalten. Aus diesem Grund habe ich auch plan_handle eingefügt. Der Vorbehalt dabei ist, dass, je nachdem, wann Sie nach dem Plan suchen, er sich möglicherweise nicht mehr im Plan-Cache befindet.

-- Remove event session if it exists
IF EXISTS (SELECT 1 FROM [sys].[server_event_sessions]
WHERE [name] = 'InterestingPlanEvents')
BEGIN
  DROP EVENT SESSION [InterestingPlanEvents] ON SERVER
END
GO
 
-- Define event session
CREATE EVENT SESSION [InterestingPlanEvents]
ON SERVER
ADD EVENT sqlserver.missing_column_statistics
(
  ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
    AND [sqlserver].[database_id]>(4))
),
ADD EVENT sqlserver.missing_join_predicate
(
  ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([sqlserver].[is_system]=(0) AND [sqlserver].[database_id]>(4))
),
ADD EVENT sqlserver.plan_affecting_convert
(
  ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
    AND [sqlserver].[database_id]>(4))
),
ADD EVENT sqlserver.unmatched_filtered_indexes
(
  ACTION(sqlserver.plan_handle,sqlserver.sql_text)
  WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
    AND [sqlserver].[database_id]>(4))
)
ADD TARGET package0.event_file
(
  SET filename=N'C:\temp\InterestingPlanEvents' /* change location if appropriate */
)
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,
TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)
GO
 
-- Start the event session
ALTER EVENT SESSION [InterestingPlanEvents] ON SERVER STATE=START;
GO

Sobald wir die Ereignissitzung eingerichtet und ausgeführt haben, können wir diese Ereignisse mit dem folgenden Beispielcode generieren. Beachten Sie, dass dieser Code von einer Neuinstallation von AdventureWorks2014 ausgeht. Wenn Sie keine haben, wird das Ereignis „missing_column_statistics“ möglicherweise nicht ausgelöst, wenn Sie in [HumanResources].[Employee] nach der Spalte [HireDate] gefragt werden.

-- These queries assume a FRESH restore of AdventureWorks2014
ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS OFF;
GO
 
USE [AdventureWorks2014];
GO
 
CREATE INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader] (
[PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]
)
WHERE [SubTotal] > 10000.00;
GO
 
/*
No join predicate
NOTE: We clear procedure here because the event ONLY fires for the *initial* compilation
*/
DBCC FREEPROCCACHE; /* Not for production use */
 
SELECT [h].[SalesOrderID], [d].[SalesOrderDetailID], [h].[CustomerID]
FROM [Sales].[SalesOrderDetail] [d],
[Sales].[SalesOrderHeader] [h]
WHERE [d].[ProductID] = 897;
GO
 
-- Columns with no statistics
SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]
FROM [HumanResources].[Employee]
WHERE [HireDate] >= '2013-01-01';
GO
 
-- Unmatched Index
DECLARE @Total MONEY = 10000.00;
 
SELECT [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]
FROM [Sales].[SalesOrderHeader]
WHERE [SubTotal] > @Total;
GO
 
-- Plan Affecting Convert
SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]
FROM [HumanResources].[Employee]
WHERE [NationalIDNumber] = 345106466;
GO
 
ALTER EVENT SESSION [InterestingPlanEvents]
ON SERVER
STATE=STOP;
GO
 
DROP EVENT SESSION [InterestingPlanEvents]
ON SERVER;
GO

HINWEIS:NACHDEM Sie Pläne aus dem Cache gezogen haben, können Sie die ALTER-Anweisung ausführen, um die Option zum automatischen Erstellen von Statistiken zu aktivieren. Wenn Sie dies an dieser Stelle tun, wird der Plan-Cache geleert und Sie müssen mit dem Testen von vorne beginnen. (Und warten Sie auch, bis Sie fertig sind, um den Index zu löschen.)

ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS ON;
GO
 
DROP INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader];
GO

Da ich die Ereignissitzung beendet habe, öffne ich die Ausgabedatei in SSMS, um zu sehen, was wir erfasst haben:

Ausgabe von erweiterten Ereignissen

Bei unserer ersten Abfrage mit einem fehlenden Join-Prädikat wird ein Ereignis angezeigt, und ich kann den Text für die Abfrage im Feld sql_text sehen. Was ich jedoch wirklich möchte, ist, mir auch den Plan anzusehen, damit ich das plan_handle nehmen und sys.dm_exec_query_plan überprüfen kann:

SELECT query_plan FROM sys.dm_exec_query_plan
(0x06000700E2200333405DD12C0000000001000000000000000000000000000000000000000000000000000000);

Und das im SQL Sentry Plan Explorer öffnen:

Join-Prädikat fehlt

Der Plan hat einen visuellen Indikator für das fehlende Join-Prädikat in der verschachtelten Schleife (das rote X), und wenn ich den Mauszeiger darüber bewege, sehe ich die Warnung (und sie ist im XML für den Plan enthalten). Ausgezeichnet ... Ich kann jetzt mit meinen Entwicklern darüber sprechen, diese Abfrage umzuschreiben.

Das nächste Ereignis betrifft eine fehlende Spaltenstatistik. Ich habe diese Situation vollständig erzwungen, indem ich AUTO_CREATE_STATISTICS für die AdventureWorks2014-Datenbank deaktiviert habe. Ich empfehle dies in keiner Weise, Form oder Form. Diese Option ist standardmäßig aktiviert und ich empfehle, sie immer aktiviert zu lassen. Das Deaktivieren ist jedoch der einfachste Weg, dieses Ereignis zu generieren. Ich habe die Abfrage wieder im Feld sql_text, aber ich verwende wieder plan_handle, um den Plan abzurufen:

SELECT query_plan FROM sys.dm_exec_query_plan
(0x060007004448323810921C360000000001000000000000000000000000000000000000000000000000000000);

Fehlende Statistik

Und wir haben wieder einen visuellen Hinweis (das gelbe Dreieck mit dem Ausrufezeichen), der darauf hinweist, dass es ein Problem mit dem Plan gibt, und wieder ist es im XML. Von hier aus würde ich zuerst prüfen, ob AUTO_CREATE_STATISTICS deaktiviert ist, und wenn nicht, würde ich die Abfrage in Management Studio ausführen, um zu sehen, ob ich die Warnung neu erstellen (und die zu erstellenden Statistiken erzwingen) kann.

Jetzt sind die verbleibenden Ereignisse etwas interessanter.

Sie werden feststellen, dass wir drei unmatched_filtered_indexes-Ereignisse haben. Ich muss noch herausfinden, warum, aber ich arbeite daran und werde in den Kommentaren posten, ob/wenn ich es sortiert bekomme. Im Moment reicht es, dass ich das Ereignis habe, und innerhalb des Ereignisses können wir auch Objektinformationen sehen, damit ich den betreffenden Index kenne:

NCI_SalesOrderHeader-Index wird durch fehlendes Indexereignis referenziert

Und ich kann wieder das plan_handle nehmen, um den Abfrageplan zu finden:

Nicht übereinstimmender Index

Dieses Mal sehe ich die Warnung im SELECT-Operator, also weiß ich, dass ich etwas weiter untersuchen muss. In diesem Fall haben Sie Optionen, um den Optimierer dazu zu bringen, den gefilterten Index zu verwenden, wenn Sie Parameter verwenden, und ich empfehle, Aarons Post zu lesen, um weitere Informationen zur Verwendung gefilterter Indexe zu erhalten.

Schließlich haben wir neun Ereignisse für plan_affecting_convert. Was zum Teufel? Ich bin immer noch dabei, das herauszufinden, aber ich habe die Option „Kausalität verfolgen“ für meine Ereignissitzung (beim Testen) verwendet, um zu bestätigen, dass alle Ereignisse Teil derselben Aufgabe sind (das sind sie). Wenn Sie sich das Ausdruckselement in der Ausgabe ansehen, sehen Sie, dass es sich leicht ändert (ebenso wie compile_time), und dies wird sichtbar, wenn Sie sich die Details der Warnung im Plan Explorer von SQL Sentry ansehen (siehe zweiter Screenshot unten). Innerhalb der Ereignisausgabe ist das Ausdruckselement does Sagen Sie uns, um welche Spalte es sich handelt, das ist ein Anfang, aber nicht annähernd genug Information, also müssen wir uns noch einmal den Plan besorgen:

SELECT query_plan FROM sys.dm_exec_query_plan
(0x0600070023747010E09E1C360000000001000000000000000000000000000000000000000000000000000000);

Auswirkungen auf die Konvertierung planen

Conversion-Details aus dem Plan

Wir sehen wieder unseren Freund, das gelbe Dreieck, im SELECT-Operator, und im XML finden wir das PlanAffectingConvert-Attribut. Dieses Attribut wurde im Showplan-Schema von SQL Server 2012 hinzugefügt. Wenn Sie also eine frühere Version ausführen, wird dies im Plan nicht angezeigt. Das Beheben dieser Warnung erfordert möglicherweise etwas mehr Arbeit – Sie müssen verstehen, wo und warum Datentypen nicht übereinstimmen, und dann entweder mit der Änderung des Codes oder des Schemas beginnen … beides kann auf Widerstand stoßen. Jonathan hat einen Beitrag, in dem die implizite Konvertierung ausführlicher behandelt wird. Dies ist ein guter Ausgangspunkt, wenn Sie zuvor noch nicht mit Konvertierungsproblemen gearbeitet haben.

Zusammenfassung

Die Ereignisbibliothek für erweiterte Ereignisse wächst weiter, und bei der Problembehandlung in SQL Server sollten Sie berücksichtigen, ob Sie die gesuchten Informationen auf andere Weise erhalten können. Vielleicht, weil es einfacher ist (ich bevorzuge XE auf jeden Fall XML!), oder weil es effizienter ist oder Ihnen mehr Details bietet. Unabhängig davon, ob Sie proaktiv nach Abfrageproblemen in Ihrer Umgebung suchen oder auf ein Problem reagieren, das jemand gemeldet hat, aber Probleme haben, es zu finden, erweiterte Ereignisse sind eine praktikable Option, die Sie in Betracht ziehen sollten, insbesondere da SQL Server um weitere neue Funktionen erweitert wird.