Взаимоблокировка (deadlock) — это ситуация в работе СУБД, при которой две транзакции взаимно ожидают освобождения ресурсов, захваченных друг другом. В контексте 1С:Предприятия это часто происходит при работе распределенных информационных баз (РИБ) или при одновременной записи документов несколькими пользователями. Рассмотрим причины возникновения этой ошибки и разберем по шагам методы её устранения, основываясь на технических данных технологического журнала (ТЖ) и системных отчетах SQL Server.
Проанализируем ситуацию, описанную в технологическом журнале. Ошибка native=1205 прямо указывает на то, что Microsoft SQL Server выбрал транзакцию в качестве «жертвы» взаимоблокировки. В нашем случае конфликт происходит в момент работы регламентного задания обмена и интерактивной работы пользователя в форме документа ПоступлениеТоваров.
Выясним причину конфликта. В конфигурациях на обычных формах часто используется автоматический режим управления блокировками. В этом режиме 1С транслирует запросы к СУБД с уровнем изоляции Serializable. Это означает следующее:
Особую сложность добавляет работа РИБ. При записи любого объекта (например, номенклатуры или документа) система автоматически делает запись в таблицу регистрации изменений (план обмена). Регламентное задание обмена в это же время может читать эту таблицу. Таким образом, возникают перекрестные захваты ресурсов таблиц объектов и таблиц регистрации изменений.
Самый быстрый способ предотвратить дедлок в автоматическом режиме — это принудительное повышение блокировки с разделяемой (S) до исключительной (U или X) в самом начале транзакции. Разберем, как это реализовать.
Вместо того чтобы позволить платформе сначала прочитать данные «просто так», мы выполним пустой запрос с конструкцией ДЛЯ ИЗМЕНЕНИЯ. Это заставит SQL Server сразу захватить ресурс в режиме, несовместимом с другими пишущими транзакциями. Посмотрим на пример кода, который необходимо разместить перед выполнением записи связанного объекта (например, характеристики или при регистрации в плане обмена):
Процедура ПередЗаписью(Отказ)
// Устанавливаем блокировку на данные, которые могут быть изменены
// одновременно другим процессом (например, обменом)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| НоменклатураИзменения.Ссылка
|ИЗ
| Справочник.Номенклатура.Изменения КАК НоменклатураИзменения
|ГДЕ
| НоменклатураИзменения.Ссылка = &Ссылка
|ДЛЯ ИЗМЕНЕНИЯ Справочник.Номенклатура.Изменения";
Запрос.УстановитьПараметр("Ссылка", ЭтотОбъект.Ссылка);
Попытка
Запрос.Выполнить();
Исключение
// Если здесь возникнет ошибка блокировки, транзакция просто подождет
КонецПопытки;
КонецПроцедуры
Важный нюанс: этот запрос должен выполняться в рамках той же транзакции, в которой происходит основная запись. Если вы записываете подчиненный объект (например, ХарактеристикаНоменклатуры) из формы основного документа, рекомендуется явно открывать транзакцию:
НачатьТранзакцию();
Попытка
// 1. Сначала блокируем 'намерения' изменения
// (Код запроса ДЛЯ ИЗМЕНЕНИЯ)
// 2. Затем выполняем запись
ХарактеристикаНоменклатурыОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Проанализируем более радикальный, но правильный с точки зрения архитектуры 1С способ — переход на управляемый режим блокировок. В этом режиме 1С использует уровень изоляции Read Committed Snapshot Isolation (RCSI) в SQL Server. Читающие транзакции больше не блокируют пишущие, так как читают данные из версий (tempdb).
Чтобы реализовать этот переход, выполним следующие шаги:
Режим управления блокировками данных в значение Управляемый (или Смешанный для постепенного перехода).
SELECT name, is_read_committed_snapshot_on FROM sys.databases WHERE name = 'ИмяВашейБазы';
0, необходимо включить этот режим (требуется монопольный доступ к базе).БлокировкаДанных во всех критических узлах (проведение документов, контроль остатков), иначе система перестанет блокировать остатки, что приведет к их уходу в минус.Иногда взаимоблокировки могут носить внутренний характер СУБД (так называемые intra-query deadlocks), когда параллельные потоки одного и того же тяжелого запроса блокируют друг друга. Рассмотрим настройку MAXDOP (Maximum Degree of Parallelism).
Для баз данных 1С:Предприятия фирма «1С» рекомендует устанавливать MAXDOP = 1. Это гарантирует, что один запрос будет выполняться одним ядром процессора, что исключает целый класс дедлоков параллелизма. Проверить и изменить настройку можно в свойствах сервера в SQL Server Management Studio (раздел Advanced) или через скрипт:
EXEC sys.sp_configure N'show advanced options', N'1';
RECONFIGURE WITH OVERRIDE;
EXEC sys.sp_configure N'max degree of parallelism', N'1';
RECONFIGURE WITH OVERRIDE;
Если ошибки продолжаются, рассмотрим способ получения графа взаимоблокировки без использования тяжелого профайлера. Воспользуемся механизмом Extended Events, который работает в SQL Server по умолчанию (сессия system_health):
package0.event_file и выберем View Target Data.xml_deadlock_report.Deadlock. Там будет наглядная схема: какие процессы (какие таблицы и индексы) не поделили ресурсы.Резюмируем: для устранения дедлоков в РИБ на старых платформах и обычных формах наиболее эффективным является сочетание программного метода ДЛЯ ИЗМЕНЕНИЯ для выравнивания порядка захвата ресурсов и технической настройки SQL Server (MAXDOP и RCSI). В долгосрочной перспективе рекомендуется переводить конфигурацию на управляемый режим блокировок данных.