Как исправить ошибку «Lock request time out period exceeded» в 1С при конфликте блокировок транзакций?

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

Каждый разработчик 1С рано или поздно сталкивается с критической ошибкой СУБД: «Microsoft SQL Server Native Client 11.0: Lock request time out period exceeded». Эта ошибка означает, что одна транзакция слишком долго ждала освобождения ресурсов, заблокированных другой транзакцией, и время ожидания блокировки (по умолчанию в 1С это 20 секунд) истекло. Давайте детально разберем физические причины возникновения этой проблемы, проанализируем поведение СУБД MS SQL Server и платформы 1С, а также рассмотрим пошаговые методы устранения конфликта блокировок.

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

В большинстве случаев проблема локализуется в коде обработчиков событий, выполняющихся в транзакции записи (например, в обработчике ПередЗаписью или ПриЗаписи). Когда в коде выполняется запрос к таблицам базы данных с ключевым словом ДЛЯ ИЗМЕНЕНИЯ, платформа транслирует этот запрос на уровень СУБД со специальными подсказками блокировки (такими как UPDLOCK). Это необходимо для того, чтобы гарантировать неизменность считываемых данных до момента завершения текущей транзакции.

Однако если база данных работает в автоматическом режиме блокировок (что крайне характерно для старых или неоптимизированных конфигураций), СУБД использует очень строгие уровни изоляции транзакций — REPEATABLE READ или SERIALIZABLE. В таких условиях неверно спроектированный запрос или структура метаданных могут парализовать работу всей системы.

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

---

Шаг 1: Настройка индексирования измерений регистра

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

Для исправления этой ситуации выполним следующие действия в конфигураторе:

  1. Откроем свойства регистра накопления, к которому обращается проблемный запрос.
  2. Перейдем на вкладку Данные и выделим измерение, по которому накладывается фильтр в запросе.
  3. В палитре свойств найдем свойство Индексировать.
  4. Установим значение Индексировать (или Индексировать с доп. упорядочиванием, если требуется сортировка).
  5. Сохраним конфигурацию базы данных.

После включения индекса СУБД построит сбалансированное дерево индекса (B-Tree). При выполнении запроса SQL Server мгновенно перейдет к нужным записям (выполнит Index Seek) и наложит блокировки обновления исключительно на целевые строки, не затрагивая данные других пользователей.

---

Шаг 2: Оптимизация конструкции «ДЛЯ ИЗМЕНЕНИЯ» в запросе

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


ВЫБРАТЬ
    РегистрВзаиморасчеты.Период,
    РегистрВзаиморасчеты.Регистратор,
    РегистрВзаиморасчеты.Сумма
ИЗ
    РегистрНакопления.Взаиморасчеты КАК РегистрВзаиморасчеты
ГДЕ
    РегистрВзаиморасчеты.Проформа = &Проформа
ДЛЯ ИЗМЕНЕНИЯ

Если этот запрос усложнить соединениями с другими таблицами, то заблокируются все связанные сущности. Чтобы этого избежать, мы должны явно указать СУБД, какую именно таблицу мы планируем изменять. Перепишем запрос правильно:


ВЫБРАТЬ
    РегистрВзаиморасчеты.Период КАК Период,
    РегистрВзаиморасчеты.Регистратор КАК Регистратор,
    РегистрВзаиморасчеты.Сумма КАК Сумма
ИЗ
    РегистрНакопления.Взаиморасчеты КАК РегистрВзаиморасчеты
ГДЕ
    РегистрВзаиморасчеты.Проформа = &Проформа
ДЛЯ ИЗМЕНЕНИЯ
    РегистрНакопления.Взаиморасчеты

В данном случае конструкция ДЛЯ ИЗМЕНЕНИЯ РегистрНакопления.Взаиморасчеты указывает платформе 1С, что блокировать нужно исключительно физическую таблицу этого регистра, оставляя другие таблицы открытыми для чтения и записи в параллельных сеансах.

---

Шаг 3: Оптимизация логики условий в секции «ГДЕ»

Разберем распространенную ошибку, когда разработчики пытаются реализовать сложную бизнес-логику непосредственно внутри SQL-запроса с помощью условного оператора ВЫБОР в секции фильтрации. Рассмотрим пример такого неэффективного кода:


ВЫБРАТЬ
    ТаблицаРегистра.Проформа,
    ТаблицаРегистра.СуммаОборота
ИЗ
    РегистрНакопления.Взаиморасчеты КАК ТаблицаРегистра
ГДЕ
    ТаблицаРегистра.Проформа = &Проформа
    И ВЫБОР
        КОГДА ТаблицаРегистра.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход) ТОГДА ИСТИНА
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ
ДЛЯ ИЗМЕНЕНИЯ
    РегистрНакопления.Взаиморасчеты

Конструкция ВЫБОР...КОНЕЦ внутри секции ГДЕ заставляет СУБД выполнять вычисления для каждой строки таблицы. Для оптимизации запроса мы должны избавиться от условных операторов и заменить их на чистые логические выражения, которые легко сопоставляются с индексами СУБД:


ВЫБРАТЬ
    ТаблицаРегистра.Проформа КАК Проформа,
    ТаблицаРегистра.СуммаОборота КАК СуммаОборота
ИЗ
    РегистрНакопления.Взаиморасчеты КАК ТаблицаРегистра
ГДЕ
    ТаблицаРегистра.Проформа = &Проформа
    И ТаблицаРегистра.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ДЛЯ ИЗМЕНЕНИЯ
    РегистрНакопления.Взаиморасчеты

Если условия действительно сложные и включают исключения по разным типам документов-регистраторов, рекомендуется разбивать такой запрос на несколько простых ветвей и объединять их с помощью оператора ОБЪЕДИНИТЬ ВСЕ. Это позволит СУБД построить эффективный план для каждого отдельного подзапроса.

---

Шаг 4: Переход на управляемые блокировки данных

Если оптимизация индексов и запросов не решает проблему полностью из-за высокой интенсивности параллельной работы пользователей, необходимо кардинально изменить архитектуру работы с транзакциями — перейти на управляемый режим блокировок.

Проанализируем, как этот переход меняет поведение системы:

  1. На уровне СУБД MS SQL Server устанавливается режим версионирования строк RCSI (Read Committed Snapshot Isolation).
  2. Уровень изоляции транзакций на SQL-сервере снижается до READ COMMITTED. При этом читающие транзакции больше никогда не блокируются пишущими, так как они считывают согласованные версии данных из системной базы tempdb.
  3. Бизнес-блокировки переносятся на уровень платформы 1С и обрабатываются специальным Менеджером управляемых блокировок в оперативной памяти сервера приложений.

Для реализации этого подхода переведем конфигурацию (или конкретный объект) в управляемый режим блокировок и опишем явную блокировку в коде на языке 1С перед выполнением запроса:


ЭлементБлокировки = Новый БлокировкаДанных;
ЭлементБлокировкиДанных = ЭлементБлокировки.Добавить("РегистрНакопления.Взаиморасчеты");
ЭлементБлокировкиДанных.УстановитьЗначение("Проформа", ПараметрПроформа);
ЭлементБлокировкиДанных.Режим = РежимБлокировкиДанных.Исключительный;

// Активируем блокировку на время транзакции
ЭлементБлокировки.Заблокировать();

// Теперь можно безопасно выполнять запрос без ключевого слова ДЛЯ ИЗМЕНЕНИЯ
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Взаиморасчеты.Сумма КАК Сумма
    |ИЗ
    |	РегистрНакопления.Взаиморасчеты КАК Взаиморасчеты
    |ГДЕ
    |	Взаиморасчеты.Проформа = &Проформа";

Запрос.УстановитьПараметр("Проформа", ПараметрПроформа);
РезультатЗапроса = Запрос.Выполнить();

Использование объекта БлокировкаДанных гарантирует, что параллельные сеансы, пытающиеся провести документы по этой же проформе, будут ожидать своей очереди на уровне сервера приложений 1С, не нагружая и не блокируя таблицы MS SQL Server.

---

Шаг 5: Глубокая диагностика с помощью Extended Events на SQL Server

Если локализовать точную причину конфликта в коде 1С не удается, настроим диагностику на стороне СУБД с помощью механизма расширенных событий (Extended Events) и отчетов о заблокированных процессах (Blocked Process Report).

Разберем по шагам процесс настройки мониторинга на MS SQL Server:

  1. Включим на SQL-сервере сбор отчетов о блокировках, которые длятся более заданного времени (например, более 5 секунд). Для этого выполним следующий T-SQL скрипт в среде SSMS:
    
    EXEC sp_configure 'show advanced options', 1;
    RECONFIGURE;
    EXEC sp_configure 'blocked process threshold', 5; -- порог в секундах
    RECONFIGURE;
    
  2. Создадим сессию Extended Events для захвата события blocked_process_report. Это событие генерирует подробный XML-отчет, содержащий информацию о блокирующем запросе (голова блокировки) и жертве блокировки (запрос, который упал по таймауту).
  3. Запустим сессию и дождемся воспроизведения ошибки в 1С.
  4. Откроем полученный лог событий. В XML-структуре отчета проанализируем узлы <blocked-process> и <blocking-process>. Там будут указаны конкретные физические таблицы СУБД (например, _AccumReg12345), их индексы и тексты SQL-запросов, сгенерированные платформой 1С.
  5. Сопоставим имена таблиц СУБД со структурой метаданных 1С (это можно сделать с помощью функции ПолучитьСтруктуруХраненияБазыДанных) и быстро найдем проблемный программный модуль.

Применяя данные методы комплексно — от базового индексирования до перехода на управляемые блокировки и детального анализа SQL-планов — мы сможем полностью исключить возникновение конфликтов блокировок и обеспечить стабильную и быструю многопользовательскую работу системы 1С.

← На главную