Почему в графе взаимоблокировок SQL Server появляются блокировки типа U в управляемом режиме 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При анализе графов взаимоблокировок (Deadlock Graph) в MS SQL Server разработчики нередко сталкиваются с ситуацией, когда транзакции удерживают или ожидают блокировку типа U (Update). Это часто вызывает недоумение, если конфигурация 1С работает в управляемом режиме блокировок данных с уровнем изоляции READ COMMITTED SNAPSHOT (RCSI). Разберем подробно внутренние механизмы СУБД и платформы 1С:Предприятие, чтобы понять, откуда берутся блокировки U и как исключить взаимоблокировки при записи регистров.

Механизм работы блокировок типа U в MS SQL Server

Рассмотрим, как СУБД выполняет операции изменения данных (команды UPDATE и DELETE). В MS SQL Server применяется двухфазный механизм модификации строк:

  1. Фаза поиска целевых записей: перед тем как изменить или удалить строку, СУБД должна найти ее в таблице или индексе. Чтобы избежать конфликтов и взаимоблокировок чтения-записи, SQL Server накладывает на просматриваемые строки или страницы блокировку типа U (блокировка обновления). Она сообщает другим транзакциям о намерении изменить данные, запрещая повторное наложение блокировок U и X, но разрешая операторам чтения без блокировок продолжать работу.
  2. Фаза модификации: в момент фактической перезаписи или удаления данных блокировка U конвертируется в исключительную блокировку X (Exclusive).

Таким образом, наличие блокировки U в графе взаимоблокировки — это абсолютно штатное поведение ядра MS SQL Server при выполнении DML-запросов, а не ошибка или признак автоматического режима блокировок 1С.

Почему режим RCSI и управляемый режим не исключают блокировки U?

Проанализируем распространенное заблуждение: версионирование строк (READ_COMMITTED_SNAPSHOT) отменяет разделяемые блокировки S (Shared) исключительно для операций чтения (SELECT). Запросы на чтение получают данные из версионного хранилища (tempdb) и не конфликтуют с операциями записи.

Однако любые операции модификации данных (INSERT, UPDATE, DELETE) продолжают использовать стандартный пессимистичный механизм СУБД. При проведении документов платформа 1С выполняет запись движений в регистры накопления или сведений. Стандартный алгоритм перезаписи набора записей включает:

  1. Удаление старых движений по регистратору (SQL-команда DELETE с наложением U-блокировок на этапе поиска удаляемых строк).
  2. Вставку новых записей (SQL-команды INSERT).
  3. Обновление таблиц итогов и агрегатов (SQL-команды UPDATE/INSERT, также использующие U и X блокировки).

Сценарии возникновения взаимоблокировок

Выясним, почему несколько параллельных транзакций записи могут войти в клинч при наличии блокировок U:

  1. Неоптимальный план выполнения запроса удаления/обновления: если для поиска строк по регистратору СУБД выбирает сканирование индекса (Index Scan) вместо точечного поиска (Index Seek), блокировка U захватывает широкий диапазон страниц. Параллельная транзакция, сканирующая этот же индекс в другом порядке, приводит к взаимному ожиданию конвертации U -> X.
  2. Эскалация блокировок (Lock Escalation): при удалении или изменении большого количества записей за одну операцию SQL Server может выполнить эскалацию построчных блокировок (KEY: U/X) до уровня страниц (PAG: U/X) или всей таблицы (TAB: U/X), блокируя соседние данные.
  3. Конфликты при обновлении итогов: одновременное проведение документов, затрагивающих одинаковые комбинации измерений регистра, вызывает параллельную модификацию одних и тех же записей в таблицах итогов.

Способы устранения взаимоблокировок в 1С

Разберем практические шаги для предотвращения взаимоблокировок на уровне 1С и СУБД.

  1. Использование явных управляемых блокировок: в управляемом режиме необходимо использовать объект БлокировкаДанных перед чтением остатков и записью движений. Это выстраивает транзакции в очередь на уровне сервера приложений 1С до того, как они дойдут до физической блокировки таблиц на SQL Server.
    
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    ЭлементБлокировки.УстановитьЗначение("Склад", ВыбранныйСклад);
    ЭлементБлокировки.ИсточникДанных = ТаблицаТоваров;
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
    Блокировка.Заблокировать();
    
  2. Детерминированный порядок записи: убедимся, что во всех модулях проведения документов наборы записей регистров модифицируются в строго одинаковой последовательности. Если в одном документе сначала пишется РегистрНакопления.ТоварыНаСкладах, а затем РегистрНакопления.ВзаиморасчетыСКонтрагентами, такой же порядок должен соблюдаться во всех остальных объектах конфигурации.
  3. Обслуживание индексов и статистики: проверим актуальность статистики и уровень фрагментации индексов на SQL Server. Корректная статистика позволяет оптимизатору выбирать Index Seek, сводя к минимуму число строк, захватываемых блокировкой U.
  4. Исключение тяжелых запросов внутри транзакции: все длительные расчеты и подготовку данных следует выносить до начала транзакции проведения, минимизируя время удержания блокировок на уровне СУБД.
← На главную