¿Los disparadores compilan cada vez?

22

Estamos solucionando problemas de un servidor que tiene una alta utilización de la CPU. Después de descubrir que las consultas realmente no lo estaban causando, comenzamos a buscar compilaciones.

El Monitor de rendimiento muestra menos de 50 compilaciones / seg y menos de 15 recompilaciones / seg.

Después de ejecutar una sesión XE en busca de compilaciones, estamos viendo miles de compilaciones por segundo.

Este sistema está utilizando disparadores para auditar los cambios. La mayoría de las compilaciones se deben a desencadenantes. Los desencadenantes hacen referencia a sys.dm_tran_active_transactions.

Nuestro primer pensamiento fue que tal vez hacer referencia a un DMV en un disparador haría que se compilara cada vez, o tal vez solo este DMV específico lo causaría. Entonces comencé a probar esa teoría. Se compila cada vez, pero no había comprobado si un disparador se compila cada vez que se dispara cuando no hace referencia al DMV y en su lugar codifica un valor. Todavía se estaba compilando cada vez que se activaba. Al soltar el gatillo se detienen las compilaciones.

  1. Estamos utilizando sqlserver.query_pre_execution_showplan en una sesión XE para rastrear las compilaciones. ¿Por qué hay una discrepancia entre eso y el contador PerfMon?
  2. ¿Es normal que obtenga un evento de compilación cada vez que se ejecuta un disparador?

Repro script:

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
fuente

Respuestas:

20

El evento XE que se está utilizando lo lleva a pensar incorrectamente que el desencadenante realmente está compilando cada ejecución. Hay dos eventos extendidos query_pre_execution_showplan y query_post_compilation_showplan que tienen descripciones similares, pero difieren en una palabra importante:

query_pre_execution_showplan

Se produce después de que se compila una instrucción SQL. Este evento devuelve una representación XML del plan de consulta estimado que se genera cuando se optimiza la consulta . El uso de este evento puede tener una sobrecarga de rendimiento significativa, por lo que solo se debe usar al solucionar problemas o monitorear problemas específicos durante breves períodos de tiempo.

query_post_compilation_showplan

Se produce después de que se compila una instrucción SQL. Este evento devuelve una representación XML del plan de consulta estimado que se genera cuando se compila la consulta . El uso de este evento puede tener una sobrecarga de rendimiento significativa, por lo que solo se debe usar al solucionar problemas o monitorear problemas específicos durante breves períodos de tiempo.

Los eventos no son exactamente los mismos en la descripción y ocurren en diferentes momentos de las pruebas posteriores con su repro. Usando una definición de sesión de evento mucho más grande, es fácil ver dónde están ocurriendo realmente las compilaciones.

ingrese la descripción de la imagen aquí

Aquí puede ver la primera compilación que ocurre para las declaraciones de inserción como planes preparados que se parametrizan automáticamente en el cuadro verde. El desencadenador se compila en el cuadro rojo y el plan se inserta en la memoria caché como se muestra en el evento sp_cache_insert. Luego, en el cuadro naranja, la ejecución del disparador obtiene un acierto en la memoria caché y reutiliza el plan del disparador para la segunda instrucción INSERT en el lote, por lo que no está compilando cada ejecución del comando INSERT y el plan se reutiliza como puede ver con el evento sp_cache_hit para el gatillo

Si ejecutamos las dos instrucciones INSERT individualmente nuevamente después de la primera ejecución, el disparador no se compila nuevamente como se muestra en los siguientes eventos:

ingrese la descripción de la imagen aquí

Aquí, la primera declaración encuentra un acierto de caché para la versión preparada con parámetros automáticos de la declaración en la memoria caché, pero se pierde el lote ad hoc que se envió. El desencadenador recibe un golpe de caché y no se vuelve a compilar como se muestra en el bloque rojo de eventos. El bloque verde de eventos repite este comportamiento para la segunda instrucción INSERT ejecutada como un lote separado. Sin embargo, en todos los casos todavía se ve el evento query_pre_execution_showplan que solo puedo atribuir a la diferencia entre ser optimizado y compilado en la descripción del evento, pero el desencadenante no se está compilando para cada ejecución como se muestra en esta serie de eventos.

Jonathan Kehayias
fuente
Si mira la primera captura de pantalla, el evento sql_batch_statistics no almacenado en caché está en la colección, pero solo se dispara para la primera ejecución del lote sql cuando se borra el caché y el plan auto-parametrizado para INSERT no está en el caché del plan. Después de eso, el evento uncached_sql_batch_statistics no se activa nuevamente.
Jonathan Kehayias
Query_post_compilation_showplan mostró solo algunas compilaciones sobre los desencadenantes, pero no la cantidad masiva que estábamos viendo en el otro evento. Encontramos algunas pepitas interesantes con query_post_compilation_showplan. Gracias por la información, Jonathan!
Tara Kizer
13

No. Los disparadores no siempre se vuelven a compilar. Sin embargo, las declaraciones de consulta simples no almacenan en caché sus planes y, por lo tanto, siempre se vuelven a compilar.

Los disparadores se vuelven a compilar si el número de filas insertadas o eliminadas cambia significativamente. Ver: https://technet.microsoft.com/en-us/library/ms181055.aspx

No sé si tienen lo mismo en XEvents, pero en SQL Trace, una recompilación tiene una subclase de evento que le dice por qué se volvió a compilar. Eso se explica en el mismo enlace de arriba.

Robert L Davis
fuente
1
Estábamos buscando compilaciones en lugar de compilaciones. Vamos a ver el servidor mañana y verificar si se debe a una simple consulta o al número de filas. ¡Gracias!
Tara Kizer