При выполнении массовых операций в 1С, таких как удаление движений по регистратору, загрузка крупных документов или пересчет итогов, программисты часто сталкиваются с проблемой конфликта блокировок. Ситуация усложняется, когда данные обрабатываются параллельно в нескольких сеансах. В таких случаях система может выдавать ошибку: "Превышено максимальное время ожидания предоставления блокировки".
В данной статье мы разберем механизмы эскалации блокировок на уровне СУБД и платформы 1С, научимся диагностировать их с помощью Технологического журнала и рассмотрим стратегии оптимизации кода для предотвращения подобных проблем.
Под эскалацией блокировок понимается процесс преобразования множества мелких блокировок (например, на уровне строк или страниц) в одну блокировку более высокого уровня (на уровне всей таблицы). Это делается СУБД для экономии оперативной памяти, так как каждая блокировка требует ресурсов.
Рассмотрим основные правила эскалации в MS SQL Server:
1211 — полностью отключает эскалацию блокировок. Однако это может привести к чрезмерному потреблению памяти и падению СУБД, если блокировок станет слишком много.1224 — отключает эскалацию по количеству (порог 5000), но оставляет возможность эскалации при нехватке оперативной памяти. Этот вариант считается более безопасным для систем 1С.Проанализируем ситуацию: если администратор СУБД подтверждает наличие эскалации на стороне SQL, установка флага 1224 может временно облегчить симптомы, но не решит коренную проблему неоптимального кода.
Важно понимать, что в современных версиях платформы 1С (8.3) существует собственный менеджер блокировок. Если ваша конфигурация работает в режиме управляемых блокировок, то конфликт может произойти еще до того, как запрос дойдет до SQL Server.
Выясним причину эскалации в 1С. Платформа имеет жестко зашитый лимит: если в одной транзакции накладывается более 100 000 блокировок на одно пространство (например, на один регистр накопления), происходит автоматическая эскалация до уровня всей таблицы. Это означает, что если вы записываете набор записей на 150 000 строк в одной транзакции, вы гарантированно заблокируете регистр для всех остальных процессов.
Чтобы точно определить, какая сессия блокирует данные и на каком объекте происходит эскалация, нам необходимо настроить Технологический журнал (ТЖ). Нам интересны события TLOCK (установка управляемой блокировки), TTIMEOUT (истечение времени ожидания) и TDEADLOCK (взаимная блокировка).
Рассмотрим пример настройки файла logcfg.xml для анализа блокировок:
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="C:\LOGS\LOCKS" history="24">
<event>
<eq property="Name" value="TLOCK"/>
</event>
<event>
<eq property="Name" value="TTIMEOUT"/>
</event>
<event>
<eq property="Name" value="TDEADLOCK"/>
</event>
<property name="all"/>
</log>
</config>
После включения сбора логов проанализируем файл. В событии TTIMEOUT нас будет интересовать поле WaitConnections — оно содержит номер сессии, которая удерживает блокировку. Найдя событие TLOCK для этой сессии, мы увидим Context (строку кода) и Regions (описание заблокированных данных). Если в описании региона указано только имя таблицы без конкретных значений измерений (организация, период и т.д.), значит, произошла эскалация.
Для устранения блокировок при массовом удалении или записи данных воспользуемся следующими рекомендациями:
Посмотрим на пример кода для установки гранулярной блокировки:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Продажи");
ЭлементБлокировки.УстановитьЗначение("Организация", ВыбраннаяОрганизация);
// Блокируем конкретный месяц, чтобы не мешать записям в другие периоды
ЭлементБлокировки.УстановитьЗначение("Период", Новый Диапазон(НачалоМесяца, КонецМесяца));
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Далее выполняем запись набора записей
Набор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Записать();
Проанализируем еще несколько подходов, которые могут помочь в специфических ситуациях:
Отключение итогов: Если выполняется массовая загрузка исторических данных, имеет смысл временно отключить использование итогов для регистра (РегистрыНакопления.ИмяРегистра.УстановитьИспользованиеИтогов(Ложь)). Это значительно ускорит запись, так как системе не придется обновлять таблицу итогов при каждой фиксации транзакции. После завершения загрузки итоги нужно будет включить и пересчитать.
Режим разделения итогов: Проверьте свойство регистра "Разделение итогов". Включение этого свойства позволяет системе записывать итоги в разные строки внутренней таблицы, что снижает вероятность блокировок при одновременной записи нескольких документов.
Анализ планов обмена и последовательностей: Иногда блокировка возникает не на самом регистре, а на вспомогательных таблицах. Если документ зарегистрирован в плане обмена или входит в последовательность, при его записи 1С будет блокировать таблицы регистраций и последовательностей. Постарайтесь минимизировать количество таких связей для технических документов с огромным количеством строк.
Таким образом, решение проблемы эскалации блокировок требует комплексного подхода: от настройки параметров СУБД до тщательного проектирования алгоритмов записи данных в коде 1С.