Skip to content

Неприятный побочный эффект OUTPUT

Пересказ статьи Erik Darling. An Unfortunate Side Effect Of OUTPUT


Время от времени я встречаю людей, которые используют OUTPUT для аудита модификаций чаще всего потому, что "триггеры - это плохо" или "триггеры медленные".

Хорошо, иногда это действительно так. Но использование OUTPUT тоже может быть неудачным решением.
Давайте посмотрим, как это может быть.

Появление процесса


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

CREATE TABLE dbo.HighQuestionScores
(
Id INT PRIMARY KEY CLUSTERED,
DisplayName NVARCHAR(40) NOT NULL,
Score BIGINT NOT NULL
);

Для тестирования процесса давайте добавим одного пользователя в таблицу:

INSERT dbo.HighQuestionScores WITH (TABLOCK)
(Id, DisplayName, Score)
SELECT u.Id, u.DisplayName, p.Score
FROM dbo.Users AS u
JOIN
(
SELECT p.OwnerUserId,
MAX(p.Score) AS Score
FROM dbo.Posts AS p
WHERE p.PostTypeId = 1
GROUP BY p.OwnerUserId
) AS p ON p.OwnerUserId = u.Id
WHERE u.Id = 22656;

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

Относящаяся к проблеме часть плана запроса представляет собой сканирование таблицы Posts:


Выполнение параллельно и занимает 1,8 секунд.

Аудит


Давайте теперь добавим предложение OUTPUT.

Я собираюсь проигнорировать вставку вывода в какую-либо структуру, поскольку хочу, чтобы вы поняли, что место вставки не имеет значения.

INSERT dbo.HighQuestionScores WITH (TABLOCK)
(Id, DisplayName, Score)
OUTPUT Inserted.Id,
Inserted.DisplayName,
Inserted.Score
SELECT u.Id, u.DisplayName, p.Score
FROM dbo.Users AS u
JOIN
(
SELECT p.OwnerUserId, MAX(p.Score) AS Score
FROM dbo.Posts AS p
WHERE p.PostTypeId = 1
GROUP BY p.OwnerUserId
) AS p ON p.OwnerUserId = u.Id
WHERE u.Id = 22656;

Относящаяся к делу часть плана теперь выглядит так:


Мы потеряли параллелизм, и изучение свойств оператора Insert говорит нам почему:



Мы получили Non Parallel Plan Reason (причина непараллельного плана). Почему нет пробелов? Я не знаю.

Почему нельзя это выполнить параллельно? Я тоже не знаю.

Как насчет триггеров?


Если мы создадим минимальный триггер на таблице, то сможем увидеть, какие накладные расходы это дает.

CREATE OR ALTER TRIGGER dbo.hqs_insert ON dbo.HighQuestionScores
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;

SELECT Inserted.Id,
Inserted.DisplayName,
Inserted.Score
FROM Inserted;
END

Давайте вернемся к оригинальному оператору Insert без Output! Нас интересуют два момента:

  • Остается ли параллельная часть плана вставки?

  • Есть ли какое-либо ограничение на параллелизм с виртуальными таблицами Inserted (и, по аналогии, с Deleted)?


Ответы в основном также положительные. План вставки по-прежнему может использовать параллелизм.

Я не буду вставлять сюда картинку, вы можете прокрутить экран вверх.

Хотя выборка из таблицы Inserted в триггере не идет параллельно, не это, похоже, ограничивает параллелизм всего плана. Видимо, чтения из таблицы Inserted не могут использовать параллелизм (типа табличной переменной в MSTVF).

Давайте немного модифицируем триггер:

TRUNCATE TABLE dbo.HighQuestionScores;
INSERT dbo.HighQuestionScores WITH (TABLOCK)
(Id, DisplayName, Score)
SELECT u.Id, u.DisplayName, p.Score
FROM dbo.Users AS u
JOIN
(
SELECT p.OwnerUserId, MAX(p.Score) AS Score
FROM dbo.Posts AS p
WHERE p.PostTypeId = 1
GROUP BY p.OwnerUserId
) AS p ON p.OwnerUserId = u.Id
WHERE u.Id < 500000;

Вот план запроса:



Чтение из Inserted является последовательным, однако остальная часть плана полностью охватывает параллелизм.

Заключение


При хорошо настроенной рабочей нагрузке вы можете не заметить каких-либо особых издержек при использовании OUTPUT для аудита определенных действий.

Конечно, если вы используете их вместе с большими вставками, и эти большие вставки работают дольше, чем хотелось бы, возможно, пора посмотреть, сколько времени они занимают без предложения OUTPUT. Вполне возможно, что использование триггера вызовет меньше проблем с производительностью.

Обратные ссылки

Нет обратных ссылок

Комментарии

Показывать комментарии Как список | Древовидной структурой

Нет комментариев.

Автор не разрешил комментировать эту запись

Добавить комментарий

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Добавленные комментарии должны будут пройти модерацию прежде, чем будут показаны.