Skip to content

Что вызывает мою скалярную функцию T-SQL (миллион раз)?

Пересказ статьи Matthew McGiffen. What is calling my T-SQL scalar function (millions of times)?


Здесь рассматривается метод, использующий расширенные события (XE) для идентификации родительских объектов, которые вызывают заданную функцию SQL и насколько часто.

Я работал в команде, когда выяснилось, что определенная функция выполнялась миллиарды раз на дню и - хотя одно выполнение почти не потребляло ресурсов - в целом это вызывало значительное использование CPU на сервере. Мы обсуждали способ улучшения ситуации, но это требовало изменения кода, который ее вызывал. Проблема заключалась в том, что эта функция использовалась примерно в 700 различных мест по всему коду базы данных - как в хранимых процедурах, так и в представлениях - хотя на сами представления могли затем ссылаться другие хранимые процедуры. Вместо того, чтобы обновлять весь код, разработчики хотели бы сначала выявить объекты, которые чаще всего выполняют эту функцию.

Здесь я покажу метод определения корневого вызывающего объекта в каждом случае и вычисления числа выполнений, которое было им спровоцировано. Следует заметить, что я должен захватывать событие для всех вызовов, сделанных в базе данных - т.к. я не знаю какое из них приводит к вызову функции - это означает, что, вероятно, будут накладные расходы ЦП, и если у вас происходит большое количество вызовов, это может привести к существенному использованию диска. В моем тесте сессия расширенного события потребляла около 130Мб дискового пространства для миллиона захваченных вызовов. Поэтому я планирую выполнять ее только для небольшого интервала, начиная с минуты, чтобы посмотреть, что я получаю, и предпочтительно выполнять это в тестовой среде с репрезентативной нагрузкой.

Метод использует расширенное событие module_start и функцию отслеживания причинно-следственной связи, которая позволяет отследить, как события связаны друг с другом. Давайте начнем и создадим сессию. Сначала я щелкну на Extended Events Sessions в SSMS, а затем щелкну правой кнопкой и выберу “New Session”.

На появившейся первой странице я просто дам сессии имя и установлю флажок Causality Tracking:



Затем я щелкну на событиях (events) и выберу нужное событие. В данном случае это просто событие module_start, и я не применяю никаких фильтров:



Наконец, я конфигурирую мое хранилище данных. Для этого примера я просто добавил ring_buffer (память) и event_file, допускающие хранилище до 10Гб. Когда я запускаю это в реальных условиях, мне может потребоваться немного больше места для хранения.



Теперь все сделано, и я могу щелкнуть ОК.

Теперь я могу запустить сессию XE в SSMS, выполнив щелчок правой кнопкой и выбрав “Start Session” (запустить сессию).

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

Выполнив мои тесты, я могу затем остановить сессию XE и экспортировать результаты в таблицу. Я показывал этот метод в предыдущей статье.

Я экспортировал результаты в новую таблицу с именем FunctionExecution в той же базе данных. Вот структура этой таблицы:



С точки зрения анализа, интерес представляют столбцы object_name (имя объекта кода), attach_activity_id.guid (это исходит от отслеживания причинно-следственных связей и будет одинаковым для всех объектов в одном стеке вызовов) и attach_activity_id.seq (показывает порядок, в котором вызывались объекты).

Для ускорения анализа я также хочу добавить пару индексов. Столбец object_name имеет тип nvarchar(max), который не поддерживает индексирование, поэтому я сначала создаю новый столбец подходящего типа данных и копирую туда значения:

ALTER TABLE dbo.FunctionExecution ADD ObjectName sysname;
UPDATE dbo.FunctionExecution SET ObjectName = CAST([object_name] AS sysname);

Затем создаю свои индексы:

CREATE INDEX IX_ObjectName ON dbo.FunctionExecution(ObjectName) INCLUDE ([attach_activity_id.guid]);
CREATE INDEX IX_GUID ON dbo.FunctionExecution([attach_activity_id.guid]) INCLUDE (ObjectName,[attach_activity_id.seq]);

Наконец, я готов выполнить запрос для получения результатов. Я ищу имя интересующей функции, использую attach_activity_id.guid для поиска других вызовов в том же самом стеке вызовов, затем использую attach_activity_id.seq для нахождения верхнего родителя (со значением 1):

SELECT parent.ObjectName, COUNT(*) AS NumberOfExecutionsOfFunction
FROM dbo.FunctionExecution func
INNER JOIN dbo.FunctionExecution parent
ON func.[attach_activity_id.guid] = parent. [attach_activity_id.guid]
WHERE func.ObjectName = 'NonInlinable'
AND parent.[attach_activity_id.seq] = 1
GROUP BY parent.ObjectName;

Вот результаты относительно тестовых процедур, которые я выполнял при запущенной сессии XE:



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

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

Ссылки по теме


  1. Захват планов выполнения только для долгоиграющих запросов

  2. Мое приложение получает ошибки тайм-аута SQL, но какой запрос вызывает проблему?

Категории: T-SQL

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

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

Комментарии

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

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

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

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

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

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