Триггеры компилируются каждый раз?

22

Мы устраняем неполадки на сервере с высокой загрузкой ЦП. Обнаружив, что запросы на самом деле не вызывают его, мы начали изучать компиляции.

Монитор производительности показывает менее 50 компиляций в секунду и менее 15 перекомпиляций в секунду.

После запуска сеанса XE в поисках компиляций мы видим тысячи компиляций в секунду.

Эта система использует триггеры для аудита изменений. Большинство сборников происходит из-за триггеров. Триггеры ссылаются на sys.dm_tran_active_transactions.

Нашей первой мыслью было, что, возможно, ссылка на DMV в триггере вызовет его компиляцию каждый раз, или, может быть, именно этот конкретный DMV вызовет его. Поэтому я начал проверять эту теорию. Он компилируется каждый раз, но я не проверял, компилируется ли триггер каждый раз, когда он срабатывает, когда не ссылается на DMV и вместо этого жестко кодирует значение. Он все еще компилировался каждый раз, когда срабатывал. Сброс триггера останавливает компиляцию.

  1. Мы используем sqlserver.query_pre_execution_showplan в сеансе XE для отслеживания компиляций. Почему существует несоответствие между этим и счетчиком PerfMon?
  2. Это нормально, что вы получаете событие компиляции каждый раз, когда запускается триггер?

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;
Тара Кизер
источник

Ответы:

20

Используемое событие XE неверно заставляет вас думать, что триггер фактически компилирует каждое выполнение. Существует два расширенных события query_pre_execution_showplan и query_post_compilation_showplan, которые имеют похожие описания, но отличаются одним важным словом:

query_pre_execution_showplan

Происходит после компиляции оператора SQL. Это событие возвращает XML-представление оценочного плана запроса, которое создается при оптимизации запроса . Использование этого события может привести к значительному снижению производительности, поэтому его следует использовать только при устранении неполадок или мониторинге конкретных проблем в течение коротких периодов времени.

query_post_compilation_showplan

Происходит после компиляции оператора SQL. Это событие возвращает XML-представление оценочного плана запроса, созданного при компиляции запроса . Использование этого события может привести к значительному снижению производительности, поэтому его следует использовать только при устранении неполадок или мониторинге конкретных проблем в течение коротких периодов времени.

События не совсем совпадают в описании и происходят в разное время по сравнению с дальнейшим тестированием с использованием вашего репро. Используя намного большее определение сеанса событий, легко увидеть, где на самом деле происходят компиляции.

введите описание изображения здесь

Здесь вы можете увидеть первую компиляцию для операторов вставки, когда подготовленные планы автоматически параметризуются в зеленом поле. Триггер скомпилирован в красном поле, и план вставляется в кеш, как показано событием sp_cache_insert. Затем в оранжевом поле выполнение триггера получает попадание в кэш и повторно использует план триггера для второго оператора INSERT в пакете, поэтому он не компилирует каждое выполнение команды INSERT, и план действительно используется повторно, как вы можете видеть с помощью события sp_cache_hit для спускового крючка.

Если после первого выполнения мы снова запустим два оператора INSERT по отдельности, триггер не скомпилируется снова, как показано в следующих событиях:

введите описание изображения здесь

Здесь первый оператор встречает попадание в кэш для подготовленной автоматически параметризованной версии оператора в кеше, но пропускает отправленный пакет adhoc. Триггер получает попадание в кэш и не компилируется снова, как показано в красном блоке событий. Зеленый блок событий повторяет это поведение для второго оператора INSERT, выполняемого как отдельный пакет. Тем не менее, в каждом случае вы по-прежнему видите срабатывание события query_pre_execution_showplan, которое я могу объяснить только разницей в оптимизации по сравнению с компиляцией в описании события, но триггер не компилируется для каждого выполнения, как показано в этой серии событий.

Джонатан Кехайяс
источник
Если вы посмотрите на первый снимок экрана, событие uncached sql_batch_statistics находится в коллекции, но срабатывает только при первом выполнении пакета sql, когда кэш очищен, а автоматически параметризованный план для INSERT отсутствует в кэше планов. После этого событие uncached_sql_batch_statistics больше не запускается.
Джонатан Кехайяс
В query_post_compilation_showplan было показано всего несколько компиляций по триггерам, но не та огромная сумма, которую мы видели в другом событии. Мы нашли некоторые интересные самородки с помощью query_post_compilation_showplan. Спасибо за информацию, Джонатан!
Тара Кизер
13

Нет. Триггеры не всегда перекомпилируются. Однако простые операторы запросов не кэшируют свои планы и поэтому всегда будут перекомпилированы.

Триггеры перекомпилируются, если количество вставленных или удаленных строк значительно изменяется. См .: https://technet.microsoft.com/en-us/library/ms181055.aspx.

Я не знаю, имеют ли они то же самое в XEvents, но в SQL Trace перекомпиляция имеет подкласс событий, который сообщает вам, почему он был перекомпилирован. Это объясняется в той же ссылке выше.

Роберт Л Дэвис
источник
1
Мы смотрели на компиляции, а не на перекомпиляции. Завтра мы рассмотрим сервер и проверим, является ли он простым оператором запроса или числом строк. Благодарность!
Тара Кизер