При параллельной работе пользователей в клиент-серверном варианте 1С:Предприятия на базе MS SQL Server часто возникает ошибка: «Конфликт блокировок при выполнении транзакции: Microsoft OLE DB Provider for SQL Server: Превышено время ожидания запроса на блокировку (native=1222)». При повторной попытке проведения документ нередко проводится успешно. Разберем детально, почему возникает эта проблема, почему простое увеличение таймаута ожидания не решает ситуацию в корне, и как правильно устранить конфликты блокировок на уровне архитектуры и кода.
Проанализируем механику работы 1С в автоматическом режиме управления блокировками данных:
SERIALIZABLE. В этом режиме СУБД накладывает S-блокировки (разделяемые) на читаемые данные и диапазоны ключей (Key-Range locks). Это означает, что чтение данных одним процессом полностью блокирует запись для других параллельных процессов, даже если они работают с разными документами, но обращаются к схожим диапазонам регистров или таблиц.TAB). В этот момент любое проведение других документов, обращающихся к той же таблице, встает в очередь ожидания.ОбработкаПроведения удерживает блокировки в СУБД недопустимо долго.Параметр «Время ожидания блокировки данных» в параметрах информационной базы определяет, сколько секунд транзакция будет ждать освобождения ресурса перед аварийным завершением по таймауту. Если мы увеличиваем это значение, ошибка действительно начинает появляться реже, но взамен пользователи получают постоянные зависания интерфейса: транзакции встают в долгие очереди, система начинает работать медленнее, а вероятность взаимных блокировок (Deadlock) многократно возрастает. Поэтому увеличение времени ожидания — это лишь временная мера, маскирующая истинную проблему.
Наиболее эффективный способ устранения эскалаций и блокировок чтения — переход на управляемый режим блокировок данных. В этом режиме на уровне MS SQL Server включается версионирование строк (уровень изоляции READ_COMMITTED_SNAPSHOT), при котором операции чтения не блокируют операции записи, а логические блокировки контролируются самим сервером 1С:
БлокировкаДанных.Рассмотрим пример установки управляемой блокировки в коде проведения:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = ТаблицаТоваров;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
Блокировка.Заблокировать();
Если блокировки вызваны конкуренцией за записи остатков по регистрам накопления или бухгалтерии, включим разделение итогов. Для этого в свойствах регистров накопления установим свойство «Использовать разделение итогов» в значение «Истина». На уровне базы данных это создает дополнительные системные разделители для записей итогов, что позволяет нескольким параллельным транзакциям одновременно изменять остатки по одним и тем же аналитикам без ожидания друг друга.
Пересмотрим алгоритм проведения, чтобы минимизировать время нахождения документа в транзакции:
ПередЗаписью, ПриЗаписи и ОбработкаПроведения отправку почты, вызовы длительных внешних сервисов и работу с диском.Нередко блокировки диапазонов возникают из-за устаревшей статистики СУБД, приводящей к выбору неоптимального плана выполнения запроса. Для поддержания корректной работы СУБД настроим регламентные задачи обслуживания базы данных:
sp_updatestats или целевого обновления UPDATE STATISTICS WITH FULLSCAN для наиболее активных таблиц регистров.Чтобы точно определить, какой именно процесс или строка кода вызывает блокировку, настроим сбор событий в Технологическом журнале 1С. Для этого настроим сбор событий TLOCKS, TTIMEOUT и TDEADLOCK с выводом свойств p:processName, WaitConnections, Context и Sql. Это позволит увидеть точный контекст кода 1С, время ожидания и текст SQL-запроса, удерживающего блокировку.