В процессе эксплуатации высоконагруженных систем на базе 1С:Зарплата и управление персоналом КОРП (редакция 3.1) специалисты часто сталкиваются с ситуациями, когда система начинает вести себя непредсказуемо: внезапно возникают ошибки «Конфликт блокировок при выполнении транзакции», процесс SQL Server начинает потреблять аномальное количество оперативной памяти, а в базе появляются «зависшие» сеансы пользователей, которых нет в консоли администрирования. В данной статье мы подробно проанализируем одну из самых коварных причин такого поведения — наличие записей с некорректными датами из далекого будущего в регистрах расчета — и разберем пошаговый алгоритм решения этой проблемы.
Рассмотрим типовую ситуацию. В один момент при попытке выполнить любое действие, связанное с записью данных (проведение больничного листа, увольнения или отпуска), пользователи начинают массово получать сообщения о конфликтах блокировок. При этом администратор замечает следующие признаки:
sqlservr.exe и rphost.exe захватывают практически всю доступную оперативную память.tempdb.Проанализируем ситуацию: если обычные методы оптимизации (обновление платформы, тестирование и исправление chdbfl.exe) не приносят результата, значит, корень проблемы кроется в самих данных, которые инициируют «тяжелые» транзакции.
Для того чтобы понять, какой именно запрос вызывает блокировку, нам необходимо настроить Технологический журнал (ТЖ). Это позволит увидеть полную картину событий в момент возникновения ошибки. Рассмотрим пример настройки файла log.cfg для отслеживания длительных запросов и блокировок:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\LOGS\LOCKS" history="24">
<event>
<eq property="Name" value="DBMSSQL"/>
<gt property="Duration" value="100000"/>
</event>
<event>
<eq property="Name" value="TLOCK"/>
</event>
<property name="all"/>
</log>
</config>
Проанализировав полученные логи, мы сможем увидеть, что блокировки происходят на уровне СУБД (событие DBMSSQL). В тексте запросов часто будут фигурировать обращения к виртуальным таблицам регистров расчета, таким как РегистрРасчета.Начисления.ФактическийПериодДействия. Если время выполнения такого запроса исчисляется десятками секунд, SQL Server автоматически выполняет эскалацию блокировок — заменяет блокировки отдельных строк блокировкой всей таблицы, парализуя работу остальных пользователей.
В некоторых случаях эскалацию блокировок можно минимизировать настройками SQL Server. Проверим использование специфических флагов трассировки, рекомендованных для работы с 1С:
tempdb на уровне страниц распределения.Однако, как показывает практика, если в базе есть «логическая бомба», эти флаги лишь немного отсрочат критический сбой, но не решат проблему полностью.
Выясним причину, почему обычные запросы в ЗУП начинают «съедать» память и диск. Часто это происходит из-за ошибочного ввода данных. Рассмотрим пример: через внешнюю обработку или из-за программной ошибки в регистры (например, РегистрРасчета.Начисления или РегистрНакопления.ДанныеОНачисленияхДляРасчетаСреднегоЗаработка) попадает запись с датой в далеком будущем (например, 01.01.2301).
Почему это убивает систему? В ЗУП реализованы сложные механизмы расчета периодов. При обращении к виртуальным таблицам за текущий период система пытается сопоставить данные, выстраивая временную шкалу от текущего момента до максимальной даты в регистре. Если эта дата — 2301 год, SQL Server пытается сформировать в памяти (и в tempdb) гигантские таблицы перерасчетов за 280 лет. Это вызывает лавинообразный рост нагрузки, блокировки и аварийное завершение рабочих процессов rphost (что объясняет «призрачные» сеансы в конфигураторе).
Чтобы найти виновника, нам нужно просканировать основные регистры на предмет дат, выходящих за разумные пределы. Мы можем воспользоваться консолью запросов или выполнить прямой запрос к SQL-таблицам (для опытных администраторов). Разберем пример запроса для поиска записей в регистре расчета:
ВЫБРАТЬ
Начисления.Регистратор,
Начисления.ПериодРегистрации,
Начисления.Сотрудник
ИЗ
РегистрРасчета.Начисления КАК Начисления
ГДЕ
Начисления.ПериодРегистрации > ДАТАВРЕМЯ(2030, 1, 1)
ИЛИ Начисления.ПериодДействияНачало > ДАТАВРЕМЯ(2030, 1, 1)
Аналогичные проверки следует провести для регистров накопления, связанных с расчетом зарплаты. После обнаружения таких записей их необходимо удалить. Внимание: удаление через SQL напрямую крайне не рекомендуется, лучше использовать стандартные средства 1С (обработку «Выгрузка и загрузка данных XML» или написание простой процедуры удаления записей по набору).
После удаления «кривых» данных проблема может не исчезнуть сразу. В ЗУП 3.1 существует механизм автоматических перерасчетов. Записи о необходимости пересчитать данные за 2301 год уже могли попасть в специальные регистры сведений. Рассмотрим, что нужно сделать:
Также рекомендуется выполнить полное обновление статистики в SQL Server, так как наличие дат из будущего сильно искажает гистограммы распределения данных, и оптимизатор SQL может продолжать выбирать неэффективные планы запросов даже после удаления записей.
Чтобы подобные инциденты не повторялись, проанализируем возможности защиты системы:
Если ПериодЗаписи > КонецГода(ТекущаяДата()) + 3600*24*365*2 Тогда // Ограничение +2 года
Сообщить("Попытка записи данных с некорректной датой!");
Отказ = Истина;
КонецЕсли;
ПередЗаписью для набора записей критически важных регистров, которая будет проверять даты и блокировать запись «будущих» периодов.Max Degree of Parallelism в значение 1 (или 2-4 для очень мощных систем). Это не позволит одному «тяжелому» запросу (вызванному ошибкой в данных) монополизировать все ядра процессора.Подводя итог, отметим: конфликты блокировок в ЗУП 3.1 далеко не всегда связаны с недостатком мощности сервера или ошибками в программном коде. Часто это результат «логической ошибки» в данных, которая заставляет механизмы расчета 1С работать в экстремальных режимах. Регулярный мониторинг дат в регистрах и настройка Технологического журнала — лучшие способы предотвратить подобные коллапсы.