Os gatilhos são compilados toda vez?

22

Estamos solucionando problemas de um servidor com alta utilização da CPU. Depois de descobrir que as consultas não estavam realmente causando isso, começamos a procurar compilações.

O Monitor de desempenho está exibindo menos de 50 compilações / s e menos de 15 recompilações / s.

Depois de executar uma sessão XE procurando compilações, estamos vendo milhares de compilações por segundo.

Este sistema está usando gatilhos para auditar alterações. A maioria das compilações ocorre devido a gatilhos. Os gatilhos referenciam sys.dm_tran_active_transactions.

Nosso primeiro pensamento foi que talvez fazer referência a um DMV em um gatilho fizesse com que ele fosse compilado a cada vez, ou talvez apenas esse DMV específico o causasse. Então eu comecei a testar essa teoria. Ele é compilado toda vez, mas eu não tinha verificado se um gatilho é compilado toda vez que é acionado quando não faz referência à DMV e, em vez disso, codifica um valor. Ainda estava compilando cada vez que era acionado. Soltar o gatilho interrompe as compilações.

  1. Estamos usando sqlserver.query_pre_execution_showplan em uma sessão XE para rastrear as compilações. Por que há uma discrepância entre isso e o contador PerfMon?
  2. É normal que você obtenha um evento de compilação cada vez que um gatilho é executado?

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
fonte

Respostas:

20

O evento XE que está sendo usado está levando você a pensar incorretamente que o gatilho está realmente compilando todas as execuções. Existem dois eventos estendidos query_pre_execution_showplan e query_post_compilation_showplan que têm descrições semelhantes, mas diferem em uma palavra importante:

query_pre_execution_showplan

Ocorre depois que uma instrução SQL é compilada. Este evento retorna uma representação XML do plano de consulta estimado que é gerado quando a consulta é otimizada . O uso desse evento pode ter uma sobrecarga de desempenho significativa, portanto, deve ser usado apenas na solução de problemas ou no monitoramento de problemas específicos por breves períodos de tempo.

query_post_compilation_showplan

Ocorre depois que uma instrução SQL é compilada. Este evento retorna uma representação XML do plano de consulta estimado que é gerado quando a consulta é compilada . O uso desse evento pode ter uma sobrecarga de desempenho significativa, portanto, deve ser usado apenas na solução de problemas ou no monitoramento de problemas específicos por breves períodos de tempo.

Os eventos não são exatamente iguais na descrição e ocorrem em momentos diferentes dos testes posteriores usando sua reprodução. Usando uma definição de sessão de evento muito maior, é fácil ver onde as compilações realmente estão acontecendo.

insira a descrição da imagem aqui

Aqui você pode ver a primeira compilação acontecendo para as instruções de inserção como planos preparados sendo parametrizados automaticamente na caixa verde. O gatilho é compilado na caixa vermelha e o plano é inserido no cache, conforme mostrado pelo evento sp_cache_insert. Em seguida, na caixa laranja, a execução do acionador recebe um acerto no cache e reutiliza o plano de acionador para a segunda instrução INSERT do lote, portanto, não está compilando todas as execuções do comando INSERT e o plano é reutilizado, como você pode ver com o evento sp_cache_hit para o gatilho.

Se executarmos as duas instruções INSERT individualmente novamente após a primeira execução, o gatilho não será compilado novamente, como mostrado nos eventos abaixo:

insira a descrição da imagem aqui

Aqui, a primeira instrução encontra um acerto de cache para a versão auto-parametrizada preparada da instrução em cache, mas um erro para o lote adhoc que foi enviado. O gatilho recebe um acerto no cache e não é compilado novamente, como mostrado no bloco vermelho de eventos. O bloco verde de eventos repete esse comportamento para a segunda instrução INSERT executada como um lote separado. No entanto, em todos os casos, você ainda vê o evento query_pre_execution_showplan disparado, que só posso atribuir à diferença de ser otimizado versus compilado na descrição do evento, mas o gatilho não está sendo compilado para todas as execuções, conforme mostrado por essa série de eventos.

Jonathan Kehayias
fonte
Se você observar a primeira captura de tela, o evento sql_batch_statistics não armazenado em cache está na coleção, mas é acionado apenas para a primeira execução do lote sql quando o cache é limpo e o plano parametrizado automaticamente para os INSERTs não está no cache do plano. Depois disso, o evento uncached_sql_batch_statistics não é acionado novamente.
Jonathan Kehayias
O query_post_compilation_showplan mostrou apenas algumas compilações nos gatilhos, mas não a enorme quantidade que estávamos vendo no outro evento. Encontramos algumas pepitas interessantes com o query_post_compilation_showplan. Obrigado pela informação, Jonathan!
Tara Kizer
13

Não. Os gatilhos nem sempre são recompilados. Instruções de consulta simples, no entanto, não obtêm seus planos em cache e, portanto, sempre seriam recompiladas.

Os gatilhos são recompilados se o número de linhas inseridas ou excluídas mudar significativamente. Consulte: https://technet.microsoft.com/en-us/library/ms181055.aspx

Não sei se eles têm o mesmo no XEvents, mas no SQL Trace, uma recompilação possui uma subclasse de evento que informa por que foi recompilada. Isso é explicado no mesmo link acima.

Robert L Davis
fonte
1
Estávamos olhando compilações em vez de recompilações. Veremos o servidor amanhã e verificamos se é uma simples instrução de consulta ou se é devido ao número de linhas. Obrigado!
Tara Kizer