Встречались ли вы с ситуацией, когда простой и быстрый запрос, который обычно выполняется за доли секунды, вдруг без видимых причин начинает "зависать" на несколько минут, а то и часов? Это распространенная проблема, которая может серьезно влиять на производительность системы. Как правило, причина кроется не в самом тексте запроса, а во внешних факторах: нагрузке на сервер, блокировках данных или неоптимальной работе СУБД.
В этой статье мы подробно разберем, как программно замерить время выполнения такого "плавающего" запроса, не прерывая его, и как использовать полученную информацию для глубокой диагностики и устранения первопричины проблемы.
Основная задача — научиться отслеживать длительность выполнения запроса, не блокируя при этом основной поток выполнения кода или пользовательский интерфейс. Если просто запустить запрос и ждать его завершения, мы не сможем ничего сделать, пока он не выполнится. Идеальным решением для этой задачи является использование фоновых заданий.
Рассмотрим пошаговый алгоритм:
ФоновыеЗадания.Выполнить().Посмотрим на пример реализации.
Допустим, у нас есть общий модуль МоиДлительныеОперации.
// В общем модуле МоиДлительныеОперации
&НаСервере
Процедура ВыполнитьПроблемныйЗапрос(ПараметрыЗапроса) Экспорт
// Здесь находится ваш код выполнения запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ";
// ... установка параметров ...
Результат = Запрос.Выполнить().Выгрузить();
// Делаем что-то с результатом
КонецПроцедуры
Теперь в том месте, где нужно выполнить запрос и проконтролировать его время, напишем следующий код. Например, в форме.
&НаКлиенте
Процедура ВыполнитьЗапросСКонтролем(Команда)
ВремяНачала = ТекущаяДата();
// Запускаем фоновое задание
Параметры = Новый Массив;
// Добавляем сюда параметры, необходимые для запроса
// Параметры.Добавить(НужныйПараметр);
ФоновоеЗадание = ЗапуститьФоновоеЗадание(Параметры);
// Подключаем обработчик ожидания для контроля
// Он будет срабатывать каждые 5 секунд
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПроверкиЗадания", ЭтотОбъект);
ПодключитьОбработчикОжидания("ПроверитьДлительностьЗадания", 5, Ложь, ОписаниеОповещения);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗапуститьФоновоеЗадание(ПараметрыВыполнения)
Возврат ФоновыеЗадания.Выполнить("МоиДлительныеОперации.ВыполнитьПроблемныйЗапрос", ПараметрыВыполнения);
КонецФункции
&НаКлиенте
Процедура ПроверитьДлительностьЗадания(ДополнительныеПараметры)
// Здесь будет логика проверки
// Предположим, что мы сохранили объект фонового задания и время начала в реквизиты формы
// Например, ЭтотОбъект.СохраненноеЗадание и ЭтотОбъект.ВремяНачалаЗадания
Если ЭтотОбъект.СохраненноеЗадание = Неопределено Тогда
// Задание еще не создано или уже завершилось
ОтключитьОбработчикОжидания("ПроверитьДлительностьЗадания");
Возврат;
КонецЕсли;
ПрошлоВремени = ТекущаяДата() - ЭтотОбъект.ВремяНачалаЗадания;
МаксимальноДопустимоеВремя = 60; // Устанавливаем порог в 60 секунд
Если ПрошлоВремени > МаксимальноДопустимоеВремя И НЕ ЭтотОбъект.СообщениеУжеОтправлено Тогда
// ВРЕМЯ ПРЕВЫШЕНО!
// Выполняем нужные действия:
// 1. Записываем сообщение в журнал регистрации
// 2. Отправляем уведомление администратору
// 3. Запускаем сбор каких-либо диагностических данных
Сообщить("Внимание! Запрос выполняется уже более " + МаксимальноДопустимоеВремя + " секунд!");
// Устанавливаем флаг, чтобы не отправлять сообщения каждую секунду
ЭтотОбъект.СообщениеУжеОтправлено = Истина;
КонецЕсли;
// Проверяем, не завершилось ли задание
СостояниеЗадания = ПолучитьСостояниеЗадания(ЭтотОбъект.СохраненноеЗадание.УникальныйИдентификатор);
Если СостояниеЗадания.Завершено Тогда
Сообщить("Задание успешно завершено!");
ОтключитьОбработчикОжидания("ПроверитьДлительностьЗадания");
ЭтотОбъект.СохраненноеЗадание = Неопределено;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСостояниеЗадания(УИД)
Возврат ФоновыеЗадания.Получить(УИД);
КонецФункции
Этот подход позволяет нам, не прерывая выполнение запроса, зафиксировать факт аномально долгой работы и инициировать сбор диагностической информации, о которой мы поговорим далее.
Итак, мы научились ловить моменты, когда запрос начинает "тормозить". Теперь наша главная задача — понять, почему это происходит. Как уже отмечалось, причина редко кроется в самом тексте запроса, если он не меняется. Проанализируем самые частые "виновники".
Это самая распространенная причина. Ваш запрос пытается прочитать или изменить данные, которые в этот же момент заблокированы другой транзакцией (например, проведением документа, расчетом себестоимости или другим отчетом). Ваш сеанс будет терпеливо ждать, пока блокировка не будет снята. Это ожидание и выглядит как "зависание".
Как обнаружить блокировки:
TLOCK (ожидание на блокировках) и TDEADLOCK (взаимные блокировки). Когда ваш запрос "зависнет", в логах ТЖ вы увидите информацию о том, какой сеанс (SessionID) и на каком объекте (Resource) ждет снятия блокировки, а также какой сеанс эту блокировку удерживает. Это даст вам точный ответ, кто кому мешает.Activity Monitor в SQL Server Management Studio может в реальном времени показать активные сессии, их запросы и информацию о блокировках (кто кого блокирует).Когда 1С отправляет запрос в СУБД, та сначала строит "план выполнения" — внутренний алгоритм, по которому она будет извлекать данные (какие индексы использовать, в каком порядке соединять таблицы и т.д.). Иногда СУБД по разным причинам может выбрать крайне неэффективный план.
Почему это происходит и что делать:
UPDATE STATISTICS).DBCC FREEPROCCACHE). Делать это нужно с осторожностью и пониманием последствий.Платформа 1С предоставляет мощные средства для анализа производительности, которые обязательно нужно использовать.
Мы уже упоминали ТЖ в контексте блокировок, но его главная сила — в отслеживании взаимодействия с СУБД. Для анализа долгих запросов настройте сбор события SDBL. В файле настройки logcfg.xml можно указать порог длительности, например, записывать в лог все запросы к базе, которые выполняются дольше 1000 миллисекунд (1 секунды).
Когда проблема воспроизведется, в логах ТЖ вы найдете:
Duration).Это позволяет со стопроцентной точностью определить, какой именно запрос тормозит и при каких обстоятельствах.
Если проблема не в одном монолитном запросе, а в сложной логике с множеством мелких запросов в цикле, поможет инструмент "Оценка производительности" из БСП. Вы можете программно или интерактивно включить замер для нужного участка кода. Система соберет детальную статистику о том, сколько раз вызывалась каждая процедура/функция и сколько суммарно времени заняло ее выполнение. Это поможет найти "узкое горлышко" в вашем алгоритме.
Для решения проблемы с "плавающими" по производительности запросами рекомендуем использовать комплексный подход:
SDBL (с порогом длительности) и TLOCK.Такой системный подход поможет не только решить текущую проблему, но и выстроить эффективный процесс мониторинга производительности вашей системы 1С.