web-dev-qa-db-fra.com

Les déclencheurs se compilent-ils à chaque fois?

Nous dépannons un serveur qui a une utilisation élevée du processeur. Après avoir découvert que les requêtes ne provoquaient pas vraiment cela, nous avons commencé à chercher des compilations.

L'analyseur de performances affiche moins de 50 compilations/s et moins de 15 recompilations/s.

Après avoir exécuté une session XE à la recherche de compilations, nous voyons des milliers de compilations par seconde.

Ce système utilise des déclencheurs pour auditer les modifications. La plupart des compilations sont dues à des déclencheurs. Les déclencheurs font référence à sys.dm_tran_active_transactions.

Notre première pensée a été que le fait de référencer un DMV dans un déclencheur le ferait compiler à chaque fois, ou peut-être que ce DMV spécifique le provoquerait. J'ai donc commencé à tester cette théorie. Il se compile à chaque fois, mais je n'avais pas vérifié si un déclencheur se compilait à chaque fois qu'il était déclenché alors qu'il ne faisait pas référence au DMV et codait en dur une valeur. Il se compilait toujours à chaque fois qu'il se déclenchait. La suppression du déclencheur arrête les compilations.

  1. Nous utilisons sqlserver.query_pre_execution_showplan dans une session XE pour suivre les compilations. Pourquoi y a-t-il un écart entre cela et le compteur PerfMon?
  2. Est-il normal que vous obteniez un événement de compilation à chaque exécution d'un déclencheur?

Script de repro:

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;
22
Tara Kizer

L'événement XE utilisé vous conduit incorrectement à penser que le déclencheur compile réellement chaque exécution. Il existe deux événements étendus query_pre_execution_showplan et query_post_compilation_showplan qui ont des descriptions similaires, mais diffèrent par un mot important:

query_pre_execution_showplan

Se produit après la compilation d'une instruction SQL. Cet événement renvoie une représentation XML du plan de requête estimé qui est généré lorsque la requête est optimisée . L'utilisation de cet événement peut entraîner une surcharge de performances importante, il ne doit donc être utilisé que lors du dépannage ou de la surveillance de problèmes spécifiques pendant de brèves périodes.

query_post_compilation_showplan

Se produit après la compilation d'une instruction SQL. Cet événement renvoie une représentation XML du plan de requête estimé qui est généré lorsque la requête est compilée . L'utilisation de cet événement peut entraîner une surcharge de performances importante, il ne doit donc être utilisé que lors du dépannage ou de la surveillance de problèmes spécifiques pendant de brèves périodes.

Les événements ne sont pas exactement les mêmes dans leur description et se produisent à des moments différents des tests ultérieurs utilisant votre repro. En utilisant une définition de session d'événement beaucoup plus large, il est facile de voir où les compilations se produisent réellement.

enter image description here

Ici, vous pouvez voir la première compilation se produire pour les instructions d'insertion en tant que plans préparés étant auto-paramétrés dans la boîte verte. Le déclencheur est compilé dans la boîte rouge et le plan est inséré dans le cache comme indiqué par l'événement sp_cache_insert. Ensuite, dans la zone orange, l'exécution du déclencheur obtient un hit de cache et réutilise le plan de déclenchement pour la deuxième instruction INSERT du lot, donc il ne compile pas chaque exécution de la commande INSERT et le plan est réutilisé comme vous pouvez le voir avec l'événement sp_cache_hit pour la gâchette.

Si nous exécutons à nouveau les deux instructions INSERT individuellement après la première exécution, le déclencheur ne se compile plus comme indiqué dans les événements ci-dessous:

enter image description here

Ici, la première instruction rencontre un hit de cache pour la version auto-paramétrée préparée de l'instruction dans le cache mais un échec pour le lot adhoc qui a été soumis. Le déclencheur obtient un hit de cache et ne se compile plus comme indiqué dans le bloc rouge des événements. Le bloc vert d'événements répète ce comportement pour la deuxième instruction INSERT exécutée en tant que lot distinct. Cependant, dans tous les cas, vous voyez toujours le déclenchement de l'événement query_pre_execution_showplan que je ne peux attribuer qu'à la différence d'être optimisé vs compilé dans la description de l'événement, mais le déclencheur ne se compile pas pour chaque exécution comme le montrent ces séries d'événements.

20

Non. Les déclencheurs ne sont pas toujours recompilés. Les instructions de requête simples, cependant, ne mettent pas leurs plans en cache et sont donc toujours recompilées.

Les déclencheurs sont recompilés si le nombre de lignes insérées ou supprimées change considérablement. Voir: https://technet.Microsoft.com/en-us/library/ms181055.aspx

Je ne sais pas s'ils ont les mêmes dans XEvents, mais dans SQL Trace, une recompilation a une sous-classe d'événements qui vous explique pourquoi elle a été recompilée. Cela est expliqué dans le même lien ci-dessus.

13
Robert L Davis