При анализе графов взаимоблокировок (Deadlock Graph) в MS SQL Server разработчики нередко сталкиваются с ситуацией, когда транзакции удерживают или ожидают блокировку типа U (Update). Это часто вызывает недоумение, если конфигурация 1С работает в управляемом режиме блокировок данных с уровнем изоляции READ COMMITTED SNAPSHOT (RCSI). Разберем подробно внутренние механизмы СУБД и платформы 1С:Предприятие, чтобы понять, откуда берутся блокировки U и как исключить взаимоблокировки при записи регистров.
Рассмотрим, как СУБД выполняет операции изменения данных (команды UPDATE и DELETE). В MS SQL Server применяется двухфазный механизм модификации строк:
Таким образом, наличие блокировки U в графе взаимоблокировки — это абсолютно штатное поведение ядра MS SQL Server при выполнении DML-запросов, а не ошибка или признак автоматического режима блокировок 1С.
Проанализируем распространенное заблуждение: версионирование строк (READ_COMMITTED_SNAPSHOT) отменяет разделяемые блокировки S (Shared) исключительно для операций чтения (SELECT). Запросы на чтение получают данные из версионного хранилища (tempdb) и не конфликтуют с операциями записи.
Однако любые операции модификации данных (INSERT, UPDATE, DELETE) продолжают использовать стандартный пессимистичный механизм СУБД. При проведении документов платформа 1С выполняет запись движений в регистры накопления или сведений. Стандартный алгоритм перезаписи набора записей включает:
DELETE с наложением U-блокировок на этапе поиска удаляемых строк).INSERT).UPDATE/INSERT, также использующие U и X блокировки).Выясним, почему несколько параллельных транзакций записи могут войти в клинч при наличии блокировок U:
Index Scan) вместо точечного поиска (Index Seek), блокировка U захватывает широкий диапазон страниц. Параллельная транзакция, сканирующая этот же индекс в другом порядке, приводит к взаимному ожиданию конвертации U -> X.KEY: U/X) до уровня страниц (PAG: U/X) или всей таблицы (TAB: U/X), блокируя соседние данные.Разберем практические шаги для предотвращения взаимоблокировок на уровне 1С и СУБД.
БлокировкаДанных перед чтением остатков и записью движений. Это выстраивает транзакции в очередь на уровне сервера приложений 1С до того, как они дойдут до физической блокировки таблиц на SQL Server.
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Склад", ВыбранныйСклад);
ЭлементБлокировки.ИсточникДанных = ТаблицаТоваров;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();
РегистрНакопления.ТоварыНаСкладах, а затем РегистрНакопления.ВзаиморасчетыСКонтрагентами, такой же порядок должен соблюдаться во всех остальных объектах конфигурации.Index Seek, сводя к минимуму число строк, захватываемых блокировкой U.