トリガーは毎回コンパイルされますか?


22

CPU使用率が高いサーバーのトラブルシューティングを行っています。クエリが実際に原因ではないことがわかった後、コンパイルを検討し始めました。

パフォーマンスモニターは、50コンパイル/秒未満および15再コンパイル/秒未満を示しています。

XEセッションを実行してコンパイルを探した後、毎秒数千のコンパイルが発生しています。

このシステムは、トリガーを使用して変更を監査しています。ほとんどのコンパイルはトリガーによるものです。トリガーはsys.dm_tran_active_transactionsを参照します。

最初に考えたのは、トリガーでDMVを参照すると毎回コンパイルされるか、この特定のDMVだけでトリガーされる可能性があるということでした。それで、私はその理論をテストし始めました。毎回コンパイルしますが、DMVを参照せず、代わりに値をハードコードするときにトリガーがトリガーされるたびにコンパイルされるかどうかはチェックしていませんでした。トリガーされるたびにコンパイルされていました。トリガーをドロップすると、コンパイルが停止します。

  1. XEセッションでsqlserver.query_pre_execution_showplanを使用してコンパイルを追跡しています。なぜそれとPerfMonカウンターの間に矛盾があるのですか?
  2. トリガーが実行されるたびにコンパイルイベントを取得するのは正常ですか?

再現スクリプト:

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;

回答:


20

使用されているXEイベントは、トリガーが実際にすべての実行をコンパイルしていると誤って考えるように導きます。2つの拡張イベントquery_pre_execution_showplanとquery_post_compilation_showplanがあり、これらは同様の説明を持ちますが、1つの重要な単語が異なります。

query_pre_execution_showplan

SQLステートメントがコンパイルされた後に発生します。このイベントは、クエリが最適化されたときに生成される推定クエリプランのXML表現を返します。このイベントを使用すると、パフォーマンスのオーバーヘッドが大きくなる可能性があるため、特定の問題を短時間でトラブルシューティングまたは監視する場合にのみ使用してください。

query_post_compilation_showplan

SQLステートメントがコンパイルされた後に発生します。このイベントは、クエリのコンパイル時に生成される推定クエリプランのXML表現を返します。このイベントを使用すると、パフォーマンスのオーバーヘッドが大きくなる可能性があるため、特定の問題を短時間でトラブルシューティングまたは監視する場合にのみ使用してください。

イベントの説明はまったく同じではなく、再現を使用したさらなるテストとは異なる時間に発生します。より大きなイベントセッション定義を使用すると、実際にコンパイルが行われている場所を簡単に確認できます。

ここに画像の説明を入力してください

ここでは、緑のボックスで自動パラメーター化された準備済みのプランとして、挿入ステートメントの最初のコンパイルが行われていることがわかります。トリガーは赤いボックスでコンパイルされ、sp_cache_insertイベントに示されているように、プランがキャッシュに挿入されます。次に、オレンジ色のボックスでトリガー実行がキャッシュヒットを取得し、バッチ内の2番目のINSERTステートメントのトリガープランを再利用するため、INSERTコマンドのすべての実行をコンパイルせず、sp_cache_hitイベントでわかるようにプランが再利用されますトリガー用。

最初の実行後に2つのINSERTステートメントを個別に再度実行すると、以下のイベントに示すように、トリガーは再びコンパイルされません。

ここに画像の説明を入力してください

ここで、最初のステートメントは、準備されたキャッシュ内のステートメントの自動パラメーター化バージョンのキャッシュヒットを検出しますが、送信されたアドホックバッチのミスを検出します。トリガーはキャッシュヒットを取得し、イベントの赤いブロックに示されているように再びコンパイルされません。緑色のイベントブロックは、別のバッチとして実行される2番目のINSERTステートメントに対してこの動作を繰り返します。ただし、いずれの場合もquery_pre_execution_showplanイベントが発生しますが、これはイベントの説明で最適化コンパイルの違いのみに起因するものですが、これらの一連のイベントで示されるように、トリガーは実行ごとにコンパイルされません。


最初のスクリーンショットを見ると、キャッシュされていないsql_batch_statisticsイベントはコレクションにありますが、キャッシュがクリアされ、INSERTの自動パラメーター化されたプランがプランキャッシュにない場合、sqlバッチの最初の実行に対してのみ発生します。その後、uncached_sql_batch_statisticsイベントは再び発生しません。
ジョナサンKehayias

query_post_compilation_showplanは、トリガーに関するいくつかのコンパイルを示しましたが、他のイベントで見られた膨大な量は示しませんでした。query_post_compilation_showplanで興味深いナゲットを見つけました。情報をありがとう、ジョナサン!
タラキーザー

13

いいえ。トリガーは常に再コンパイルされるとは限りません。ただし、単純なクエリステートメントでは、プランがキャッシュされないため、常に再コンパイルされます。

挿入または削除された行の数が大幅に変更された場合、トリガーは再コンパイルされます。参照:https : //technet.microsoft.com/en-us/library/ms181055.aspx

XEventsでそれらが同じかどうかはわかりませんが、SQLトレースでは、再コンパイルには、イベントが再コンパイルされた理由を示すイベントサブクラスがあります。上記の同じリンクで説明されています。


1
再コンパイルではなくコンパイルを検討していました。明日、サーバーを調べて、単純なクエリステートメントによるものなのか、行数によるものなのかを確認します。ありがとう!
タラカイザー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.