Как узнать, кто и почему заблокировал регистр при конфликте блокировок в 1С

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

Ошибка с текстом «Конфликт блокировок при выполнении транзакции. Превышено максимальное время ожидания» является одной из самых распространенных проблем при параллельной работе пользователей. Она означает, что одна сессия попыталась прочитать или записать данные, которые уже удерживаются другой транзакцией, и не смогла дождаться освобождения ресурса в течение установленного таймаута (по умолчанию 20 секунд).

Разберем по шагам, как локализовать виновника конфликта (пользователя, документ и конкретную строку программного кода), а также выясним причины и способы предотвращения таких ситуаций.

Определение типа блокировок: СУБД или управляемые

Прежде чем приступать к поиску, проанализируем архитектуру используемых блокировок в вашей базе данных:

  1. Автоматический режим (блокировки СУБД). Платформа 1С использует уровни изоляции транзакций SERIALIZABLE или REPEATABLE READ на стороне MS SQL Server. В этом случае блокировки накладываются самим SQL-сервером (блокируются строки, страницы или таблицы целиком). При конфликте SQL Server переводит сессию в состояние ожидания.
  2. Управляемый режим (блокировки 1С). Блокировками управляет собственный менеджер кластера серверов 1С на логическом уровне метаданных, а на уровне СУБД используется менее строгий уровень изоляции READ COMMITTED. Ошибка ожидания генерируется менеджером блокировок 1С.

Способ 1. Мониторинг активных ожиданий на уровне MS SQL Server

Если в базе используется автоматический режим блокировок, во время зависания сессий мы можем оперативно перехватить блокирующий и ожидающий SQL-запросы с помощью системных динамических представлений (DMV).

Выполним в SQL Server Management Studio (SSMS) следующий диагностический скрипт:


USE [ИмяВашейБазы];
SELECT
    db.name AS DBName,
    tl.resource_type,
    CASE WHEN tl.resource_type = 'OBJECT' THEN 
        OBJECT_NAME(tl.resource_associated_entity_id, db.database_id)
    ELSE
        OBJECT_NAME(p.OBJECT_ID, db.database_id)
    END AS BlockedObjectName,
    tl.request_session_id AS Waiting_SPID,
    wt.blocking_session_id AS Blocking_SPID,
    h1.TEXT AS RequestingText,
    h2.TEXT AS BlockingText,
    tl.request_mode
FROM sys.dm_tran_locks AS tl
    LEFT JOIN sys.databases db ON db.database_id = tl.resource_database_id
    LEFT JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
    LEFT JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
    LEFT JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
    LEFT JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
    CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
    CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
WHERE wt.blocking_session_id IS NOT NULL;

Проанализируем результат выборки:

  1. Колонка Blocking_SPID показывает номер системного процесса SQL Server, удерживающего блокировку.
  2. Колонка Waiting_SPID содержит идентификатор сессии, которая ожидает освобождения ресурса.
  3. Поле BlockingText выводит последний SQL-запрос, выполненный блокирующим пользователем.
  4. Поле BlockedObjectName содержит имя заблокированной таблицы СУБД.

Обратите внимание: данный запрос возвращает данные только в момент, когда ожидание происходит прямо сейчас. Если таймаут уже истек и транзакция аварийно завершилась, таблица будет пустой.

Способ 2. Расследование инцидентов через Технологический журнал (ТЖ)

Для гарантированного анализа постфактум настроим сбор событий Технологического журнала 1С. Это основной и самый надежный инструмент для поиска контекста ошибки в коде конфигурации.

Создадим или дополним конфигурационный файл logcfg.xml в каталоге настроек платформы (например, C:\Program Files\1cv8\conf\logcfg.xml):


<config xmlns="http://v8.1c.ru/v8/tech-log">
    <log location="C:\LOGS\1C_Locks" history="72">
        <event>
            <eq property="name" value="TLOCK"/>
        </event>
        <event>
            <eq property="name" value="TTIMEOUT"/>
        </event>
        <event>
            <eq property="name" value="TDEADLOCK"/>
        </event>
        <property name="all"/>
    </log>
</config>

Разберем ключевые свойства, которые будут зафиксированы в логах при возникновении конфликта:

  1. TTIMEOUT — событие фиксации ошибки ожидания блокировки. В нем содержится свойство WaitConnections — номер сеанса/соединения, удерживавшего нужные данные.
  2. Usr — имя пользователя 1С, выполнявшего операцию.
  3. Context — стек вызовов модулей 1С, строка кода проведения документа или функции, в которой возникло ожидание или была установлена блокировка.
  4. Regions и Locks — заблокированные области, имена таблиц и значения конкретных измерений регистра.
  5. TLOCK — события успешной установки или ожидания блокировок. Найдя по номеру WaitConnections предшествующее событие TLOCK, мы точно узнаем, кто именно держал ресурс и в какой процедуре конфигурации.

Способ 3. Использование Центра управления производительностью (ЦУП)

Если в организации развернут инструмент 1С:ЦУП (входит в состав Корпоративного инструментального пакета), процесс расследования автоматизируется:

  1. ЦУП подключается к исследуемой базе и автоматически настраивает сбор необходимых событий ТЖ и счетчиков производительности.
  2. В интерфейсе ЦУП наглядно строятся деревья блокировок, где отображаются «жертва» и «виновник» таймаута.
  3. Инструмент сразу связывает SQL-запросы со строками модулей конфигурации 1С и именами пользователей без необходимости ручного сопоставления логов.

Основные причины возникновения конфликтов и методы оптимизации

После выявления проблемного участка кода проанализируем типовые причины долгой блокировки регистров:

  1. Длительные транзакции. Внутри транзакции проведения документа выполняются тяжелые запросы, расчеты, вызовы веб-сервисов или вывод диалоговых окон пользователю. Решение: вынести все подготовительные вычисления до начала транзакции.
  2. Неправильный порядок захвата ресурсов. Если два разных документа блокируют одни и те же регистры или строки в разной последовательности, это приводит к взаимным блокировкам и таймаутам. Решение: упорядочить последовательность блокировки объектов в коде.
  3. Большие порции данных в регламентных заданиях. Запись сотен тысяч строк в одной неразделенной транзакции. Решение: выполнять порционную обработку с периодическим вызовом ЗафиксироватьТранзакцию().
  4. Перевод на управляемый режим. Переключение конфигурации на управляемые блокировки и явное использование объекта БлокировкаДанных в коде перед чтением/записью остатков исключает избыточные блокировки целых диапазонов таблиц на уровне СУБД.
  5. Разделение итогов. Для регистров накопления и бухгалтерии с высокой конкуренцией параллельной записи следует включить режим разделения итогов.
← На главную