Werden Trigger jedes Mal kompiliert?

22

Wir suchen nach einem Server mit hoher CPU-Auslastung. Nachdem wir festgestellt hatten, dass die Abfragen dies nicht wirklich verursachten, begannen wir, Kompilierungen zu untersuchen.

Der Systemmonitor zeigt weniger als 50 Kompilierungen pro Sekunde und weniger als 15 Kompilierungen pro Sekunde an.

Nach dem Ausführen einer XE-Sitzung, in der nach Kompilierungen gesucht wird, werden Tausende von Kompilierungen pro Sekunde angezeigt.

Dieses System verwendet Trigger, um Änderungen zu überwachen. Die meisten Kompilierungen sind auf Trigger zurückzuführen. Der Trigger verweist auf sys.dm_tran_active_transactions.

Unser erster Gedanke war, dass ein Verweis auf eine DMV in einem Trigger dazu führen könnte, dass sie jedes Mal kompiliert wird, oder dass dies möglicherweise nur durch diese spezifische DMV verursacht wird. Also fing ich an, diese Theorie zu testen. Es wird jedes Mal kompiliert, aber ich hatte nicht geprüft, ob ein Trigger jedes Mal kompiliert wird, wenn er nicht auf die DMV verweist und stattdessen einen Wert fest codiert. Es wurde immer noch jedes Mal kompiliert, wenn es ausgelöst wurde. Durch das Löschen des Auslösers werden die Kompilierungen gestoppt.

  1. Wir verwenden sqlserver.query_pre_execution_showplan in einer XE-Sitzung, um die Kompilierungen zu verfolgen. Warum besteht eine Diskrepanz zwischen diesem und dem PerfMon-Zähler?
  2. Ist es normal, dass Sie jedes Mal, wenn ein Trigger ausgeführt wird, ein Kompilierungsereignis erhalten?

Repro-Skript:

CREATE TABLE t1 (transaction_id int, Column2 varchar(100));
CREATE TABLE t2 (Column1 varchar(max), Column2 varchar(100));
GO

CREATE TRIGGER t2_ins
ON t2
AFTER INSERT
AS

INSERT INTO t1
SELECT (SELECT TOP 1 transaction_id FROM sys.dm_tran_active_transactions), Column2
FROM inserted;
GO

--Both of these show compilation events
INSERT INTO t2 VALUES ('row1', 'value1');
INSERT INTO t2 VALUES ('row2', 'value2');
GO

ALTER TRIGGER t2_ins
ON t2
AFTER INSERT
AS

INSERT INTO t1
SELECT 1000, Column2
FROM inserted;
GO

--Both of these show compilation events
INSERT INTO t2 VALUES ('row3', 'value3');
INSERT INTO t2 VALUES ('row4', 'value4');

DROP TRIGGER t2_ins;

--These do not show compilation events
INSERT INTO t2 VALUES ('row5', 'value5');
INSERT INTO t2 VALUES ('row6', 'value6');

DROP TABLE t1, t2;
Tara Kizer
quelle

Antworten:

20

Das verwendete XE-Ereignis lässt Sie fälschlicherweise vermuten, dass der Trigger tatsächlich jede Ausführung kompiliert. Es gibt zwei erweiterte Ereignisse query_pre_execution_showplan und query_post_compilation_showplan, die ähnliche Beschreibungen haben, sich jedoch durch ein wichtiges Wort unterscheiden:

query_pre_execution_showplan

Tritt ein, nachdem eine SQL-Anweisung kompiliert wurde. Dieses Ereignis gibt eine XML-Darstellung des geschätzten Abfrageplans zurück, der bei der Optimierung der Abfrage generiert wird . Die Verwendung dieses Ereignisses kann einen erheblichen Leistungsaufwand verursachen. Daher sollte es nur zur Fehlerbehebung oder Überwachung bestimmter Probleme für kurze Zeiträume verwendet werden.

query_post_compilation_showplan

Tritt ein, nachdem eine SQL-Anweisung kompiliert wurde. Dieses Ereignis gibt eine XML-Darstellung des geschätzten Abfrageplans zurück, der beim Kompilieren der Abfrage generiert wird . Die Verwendung dieses Ereignisses kann einen erheblichen Leistungsaufwand verursachen. Daher sollte es nur zur Fehlerbehebung oder Überwachung bestimmter Probleme für kurze Zeiträume verwendet werden.

Die Ereignisse stimmen in der Beschreibung nicht genau überein und treten zu anderen Zeitpunkten auf als bei weiteren Tests unter Verwendung Ihres Repros. Mit einer viel größeren Definition der Ereignissitzung können Sie leicht erkennen, wo Kompilierungen tatsächlich stattfinden.

Bildbeschreibung hier eingeben

Hier sehen Sie die erste Kompilierung für die Einfügeanweisungen als vorbereitete Pläne, die in der grünen Box automatisch parametrisiert werden. Der Trigger wird im roten Feld kompiliert und der Plan wird wie durch das Ereignis sp_cache_insert angezeigt in den Cache eingefügt. In der orangefarbenen Box erhält die Triggerausführung einen Cache-Treffer und verwendet den Triggerplan für die zweite INSERT-Anweisung im Batch erneut. Daher wird nicht jede Ausführung des INSERT-Befehls kompiliert und der Plan wird erneut verwendet, wie Sie anhand des Ereignisses sp_cache_hit sehen können für den Abzug.

Wenn wir die beiden INSERT-Anweisungen nach der ersten Ausführung einzeln erneut ausführen, wird der Trigger nicht erneut kompiliert, wie in den folgenden Ereignissen gezeigt:

Bildbeschreibung hier eingeben

Hier trifft die erste Anweisung auf einen Cache-Treffer für die vorbereitete automatisch parametrisierte Version der Anweisung im Cache, jedoch auf einen Fehler für den übermittelten Ad-hoc-Batch. Der Trigger erhält einen Cache-Treffer und wird nicht erneut kompiliert, wie im roten Ereignisblock gezeigt. Der grüne Ereignisblock wiederholt dieses Verhalten für die zweite INSERT-Anweisung, die als separater Stapel ausgeführt wird. In jedem Fall sehen Sie jedoch immer noch das Ereignis query_pre_execution_showplan, das ich nur auf den Unterschied zwischen der Optimierung und der Kompilierung in der Ereignisbeschreibung zurückführen kann. Der Trigger wird jedoch nicht für jede Ausführung kompiliert, wie in diesen Ereignissen dargestellt.

Jonathan Kehayias
quelle
Wenn Sie sich den ersten Screenshot ansehen, befindet sich das nicht zwischengespeicherte Ereignis sql_batch_statistics in der Auflistung, wird jedoch nur für die erste Ausführung des SQL-Stapels ausgelöst, wenn der Cache geleert wird und sich der automatisch parametrisierte Plan für die INSERTs nicht im Plan-Cache befindet. Danach wird das Ereignis uncached_sql_batch_statistics nicht erneut ausgelöst.
Jonathan Kehayias
Der query_post_compilation_showplan zeigte nur einige Zusammenstellungen zu den Auslösern, aber nicht die massive Menge, die wir beim anderen Ereignis sahen. Mit query_post_compilation_showplan haben wir einige interessante Nuggets gefunden. Danke für die Information, Jonathan!
Tara Kizer
13

Trigger werden nicht immer neu kompiliert. Einfache Abfrageanweisungen werden jedoch nicht zwischengespeichert und werden daher immer neu kompiliert.

Trigger werden neu kompiliert, wenn sich die Anzahl der eingefügten oder gelöschten Zeilen erheblich ändert. Siehe: https://technet.microsoft.com/en-us/library/ms181055.aspx

Ich weiß nicht, ob sie in XEvents identisch sind, aber in SQL Trace verfügt eine Neukompilierung über eine Ereignisunterklasse, die angibt, warum sie neu kompiliert wurde. Das wird im selben Link oben erklärt.

Robert L. Davis
quelle
1
Wir haben uns eher mit Kompilierungen als mit Neukompilierungen befasst. Wir werden uns morgen den Server ansehen und prüfen, ob es sich um eine einfache Abfrageanweisung handelt oder ob es sich um die Anzahl der Zeilen handelt. Vielen Dank!
Tara Kizer