Как устранить конфликт блокировок SQL Server при расчете себестоимости в фоновом режиме?

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

При выполнении тяжелых регламентных операций в 1С, таких как Расчет себестоимости выпуска, программисты часто сталкиваются с ошибкой Конфликт блокировок при выполнении транзакции. Ситуация осложняется тем, что ошибка Lock request time out period exceeded (native=1222) может возникать даже в том случае, если в базе данных в этот момент работает только один пользователь. Рассмотрим подробно, почему это происходит и как найти решение задачи.

Анализируем причину ошибки: почему возникает таймаут?

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

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

Влияние контекста клиента на фоновые задания

Когда мы используем метод ДлительныеОперации.ВыполнитьСКонтекстомКлиента, система передает в фоновое задание параметры сеанса и временные таблицы. Если перед запуском фонового задания в основном сеансе была открыта транзакция или объект (документ) был заблокирован для записи, фоновое задание может «споткнуться» об эту блокировку.

Разберем, как это выглядит в коде. Часто разработчики по ошибке пишут конструкцию вида:


НачатьТранзакцию();
// Какие-то действия с документом
Результат = ДлительныеОперации.ВыполнитьВФоне(...);
ЗафиксироватьТранзакцию();

В этой ситуации фоновое задание пытается записать данные в регистр, но основная транзакция еще не завершена и удерживает блокировку. Никогда не запускайте длительные фоновые операции внутри открытой транзакции.

Эскалация блокировок в MS SQL Server

Одной из скрытых причин является механизм эскалации блокировок (Lock Escalation). При записи огромного количества строк (например, при расчете себестоимости) SQL Server может решить, что блокировать каждую строку по отдельности слишком накладно, и заблокирует всю таблицу целиком.

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

  1. Проверьте настройки индексов в SQL Server Management Studio. Для таблиц регистров должны быть разрешены блокировки строк (ALLOW_ROW_LOCKS = ON) и страниц (ALLOW_PAGE_LOCKS = ON).
  2. В некоторых случаях помогает установка флага трассировки 1211 на стороне SQL Server, который полностью отключает эскалацию блокировок до уровня таблицы (но будьте осторожны, это увеличивает потребление памяти процессом SQL).

Диагностика с помощью Технологического журнала

Если визуальный анализ кода не помогает, необходимо настроить Технологический журнал (ТЖ). Это позволит точно увидеть, какой именно процесс установил блокировку и какой ее ожидает. Для этого создадим файл logcfg.xml со следующим содержанием:


<config xmlns="http://v8.1c.ru/7.4/config/admin/vrs/logcfg">
  <log location="C:\LOGS\LOCKS" history="2">
    <event>
      <eq property="Name" value="TTIMEOUT"/>
    </event>
    <event>
      <eq property="Name" value="TLOCK"/>
      <p:conf property="WaitConnections"/>
    </event>
    <property name="all"/>
  </log>
</config>

Рассмотрим, что мы получим в логах: событие TTIMEOUT покажет момент возникновения ошибки, а в описании события TLOCK будет указан номер сеанса (WaitConnections), который удерживал ресурс. Это позволит однозначно определить «виновника» — будь то другое фоновое задание, полнотекстовый поиск или зависший сеанс.

Использование SQL Profiler для поиска причин

Если у вас есть доступ к серверу баз данных, воспользуемся SQL Profiler. Нам необходимо отследить событие Lock:Timeout. Разберем по шагам:

  1. Запустите SQL Profiler и выберите стандартный шаблон (Standard).
  2. В фильтрах событий (Events Selection) отметьте Errors and Warnings -> Exception и Locks -> Lock:Timeout.
  3. Запустите проведение документа.
  4. Как только выполнение прервется, проанализируйте колонку SPID (ID процесса в SQL). Вы сможете увидеть SQL-запрос, который не смог выполниться, и сопоставить его с таблицей 1С.

Практические рекомендации по решению

Подведем итог и составим план действий для исправления ситуации:

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

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

← На главную