При переходе на управляемые блокировки разработчики нередко сталкиваются с ситуацией: в коде явно создается объект БлокировкаДанных, пространство блокировок разделено по разным значениям измерений, однако при проведении двух параллельных документов один из сеансов завершается аварийно с системным исключением СУБД:
Microsoft SQL Server Native Client 11.0: Превышено время ожидания запроса на блокировку. HRESULT=80040E31, SQLSrvr: SQLSTATE=HYT00, state=33, Severity=10, native=1222, line=1
Разберем детально, почему так происходит, как работают уровни блокировок в связке «1С — MS SQL Server» и какие настройки метаданных приводят к взаимным ожиданиям.
Проанализируем природу сообщения об ошибке. Код ошибки native=1222 (Lock request time out period exceeded) формируется непосредственно сервером Microsoft SQL Server. Это свидетельствует о следующем:
Объект у двух документов не пересеклись, и платформа разрешила обеим транзакциям продолжить выполнение.Рассмотрим типовой фрагмент кода, с которого начинается подобная проблема:
Движения.рс_Рег1.Записывать = Истина;
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить();
ЭлементБлокировки.Область = "РегистрНакопления.рег1";
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = тч;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Объект", "Объект");
Блокировка.Заблокировать();
Если после вызова метода Заблокировать() установить точку останова в первом сеансе, а во втором попытаться провести аналогичный документ с другими объектами, то второй сеанс «зависнет» и упадет по таймауту СУБД. Выясним, что именно вызывает эту блокировку в базе данных.
Обратим внимание на режим очистки движений документа. Если в свойствах документа на закладке «Движения» установлено значение Удалять автоматически (так называемая старая методика проведения), платформа выполняет следующие действия:
ОбработкаПроведения платформа выполняет прямой SQL-запрос DELETE по всем регистрам, где документ выступает регистратором.Table Scan или широкий Index Scan).DELETE своих собственных старых движений.MS SQL Server распределяет данные по страницам объемом 8 КБ. Если несколько записей разных документов попадают на одну физическую страницу данных или в один узел индекса, блокировка накладывается не только на конкретную строку, но и на всю страницу (PAG). Кроме того, если транзакция модифицирует множество строк, СУБД может автоматически повысить уровень блокировки со строк до таблицы (Lock Escalation).
Длительное удержание транзакции открытой (например, при отладке точкой останова или при тяжелых расчетах) гарантированно приводит к таймаутам любых параллельных операций записи в этот же регистр.
Ответим на ключевой вопрос архитектуры: для чего вообще накладывается явная блокировка?
Единственная цель наложения блокировки (как разделяемой, так и исключительной) — гарантировать, что прочитанные данные не будут изменены другой параллельной транзакцией до момента завершения текущей транзакции. Иными словами, блокировка необходима исключительно тогда, когда перед записью выполняется чтение (например, контроль остатков).
Если документ просто формирует обороты или движения без предварительного чтения остатков из этого регистра, накладывать объект БлокировкаДанных не требуется вовсе. Лишний вызов Заблокировать() создает дополнительную нагрузку на менеджер блокировок платформы.
Разберем по шагам, как устранить конфликты блокировок и повысить параллельность проведения документов:
DELETE перед началом проведения. Новые движения при вызове Записать() набора записей просто перезаписывают существующие, что кардинально снижает нагрузку на СУБД и исключает захват всей таблицы регистра.ОбработкаПроведения нет чтения данных регистра (проверки остатков, сверки лимитов), удалите создание объекта БлокировкаДанных. Запись движений в управляемом режиме транзакции сама по себе обеспечит целостность на уровне СУБД.Управляемый не только на уровне конфигурации или регистра, но и у самого проводящегося документа. В смешанном режиме конфигурации режим транзакции определяется именно объектом, который инициирует транзакцию.Если таймаут native=1222 сохраняется в рабочей базе, выполните анализ ожиданий в MS SQL Server с помощью динамических административных представлений. Пока сеанс ожидает блокировку, выполните в среде SQL Server Management Studio запрос:
SELECT
wt.blocking_session_id AS [БлокирующийСеанс],
wt.session_id AS [ОжидающийСеанс],
wt.wait_type AS [ТипОжидания],
wt.wait_duration_ms AS [ВремяОжидания_мс],
tl.resource_type AS [ТипРесурса],
OBJECT_NAME(tl.resource_associated_entity_id) AS [ИмяТаблицы]
FROM sys.dm_os_waiting_tasks wt
JOIN sys.dm_tran_locks tl ON wt.resource_address = tl.lock_owner_address
WHERE wt.wait_type LIKE 'LCK_%';
С помощью полученного ИмяТаблицы вы сможете точно определить, на какой именно таблице происходит конфликт: на таблице движений регистра, таблице итогов, служебной таблице последовательностей или таблице регистрации изменений планов обмена.