Нередко разработчики 1С сталкиваются со специфическим поведением системы: при первом запуске алгоритма расчет и запись данных выполняются за пару минут, но при повторном запуске той же команды с идентичными параметрами тот же самый запрос зависает на десятки минут или даже часов. Проанализируем природу этой проблемы, выясним причины деградации плана выполнения на стороне СУБД MS SQL Server и разберем практические способы устранения «залипания» запросов.
Рассмотрим типовой сценарий, описанный в обсуждении: внешняя обработка считывает данные из независимого непериодического регистра сведений, рассчитывает зависимые показатели и выполняет перезапись набора записей обратно в регистр. Первый расчет занимает около минуты, повторный — зависает на этапе выполнения пакетного запроса. При этом монитор ресурсов операционной системы показывает наличие свободной оперативной памяти и отсутствие физических блокировок со стороны других пользователей.
Причина такого поведения кроется в особенностях работы оптимизатора запросов MS SQL Server и кэширования планов выполнения (механизм Parameter Sniffing):
Hash Match или Merge Join), и расчет завершается быстро.Nested Loops (вложенные циклы) с многократным сканированием таблиц (Index Scan или Table Scan).ARITHABORT или ANSI_NULLS) в контексте внешнего соединения или фонового вызова могут отличаться от интерактивной консоли, заставляя сервер компилировать отдельный план.Если тяжелый расчет строится на пакетном запросе с использованием временных таблиц, необходимо обратить пристальное внимание на их структуру и индексацию. Оптимизатор MS SQL строит общий план выполнения всего пакета заранее, еще до того, как временные таблицы физически наполнятся данными.
Разберем типичные ошибки и правила их исправления:
Nested Loops. Если строк во временной таблице оказалось больше ожидаемого, время выполнения возрастает в тысячи раз. Всегда используйте предложение ИНДЕКСИРОВАТЬ ПО для полей, участвующих в условиях ПО и ГДЕ:
ВЫБРАТЬ
ИсходныеДанные.Идентификатор КАК Идентификатор,
ИсходныеДанные.Показатель КАК Показатель
ПОМЕСТИТЬ ВТ_ДанныеДляРасчета
ИЗ
РегистрСведений.ЗначенияПоказателей КАК ИсходныеДанные
ИНДЕКСИРОВАТЬ ПО
Идентификатор;
МенеджерВременныхТаблиц, выполняя промежуточную выборку, а затем освобождая ресурсы явным методом МенеджерВременныхТаблиц.Закрыть(). Это позволит СУБД переоценивать объемы данных между вызовами.В СУБД MS SQL включен автоматический пересчет статистики (AUTO_UPDATE_STATISTICS), но он срабатывает только при изменении определенного процента строк таблицы (по умолчанию — около 20% плюс 500 строк). Если расчет изменяет значимый, но недостаточный для триггера объем данных, распределение индексов кардинально меняется, а статистика считается «актуальной».
Для стабилизации повторных расчетов рассмотрим следующие шаги:
ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = OFF;.LEGACY_CARDINALITY_ESTIMATION = ON) в ряде случаев сразу устраняет неадекватные планы.Физически таблица непериодического независимого регистра сведений в MS SQL имеет кластерный индекс, порядок полей которого строго повторяет последовательность измерений, заданных в дереве метаданных конфигуратора.
Проанализируем правила работы с индексами регистра:
ГДЕ или в соединениях отбор идет по первому измерению регистра, затем по второму и так далее. Если в запросе накладывается условие по второму измерению, а первое не участвует в фильтре, СУБД выполняет полное сканирование таблицы (Index Scan) вместо точечного поиска (Index Seek).Для исключения неоптимального распараллеливания тяжелых запросов проверим ключевые параметры инстанса MS SQL Server:
Max Degree of Parallelism должен быть равен 1 (параллелизм отключен), либо значение не должно превышать 2-4 с увеличением Cost Threshold for Parallelism до 30-50. Параллельные планы на плохой статистике способны приводить к колоссальным потерям времени на ожидании CXPACKET.tempdb должна быть разбита на несколько файлов данных равного размера (соответственно числу физических ядер сервера, но не более 8), чтобы исключить очереди за страницами распределения пространства памяти.