Как устранить конфликт блокировок при выполнении транзакции в 1С 8.3?

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

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

Разберем причины возникновения конфликтов блокировок

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

Основные причины конфликтов:

  1. Длительные транзакции: чем дольше выполняется код между началом транзакции и её завершением, тем выше вероятность, что другой пользователь "столкнется" с заблокированными данными.
  2. Неправильный порядок обращения к объектам: если один процесс сначала блокирует Регистр А, а затем Регистр Б, а второй процесс — наоборот, возникает Deadlock (взаимная блокировка).
  3. Избыточное чтение в транзакции: чтение данных с установкой блокировки там, где это не требуется.
  4. Отсутствие индексов: СУБД (например, MS SQL Server) может заблокировать всю таблицу вместо конкретной строки, если не может быстро найти запись по индексу.

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

Рассмотрим основной способ решения проблемы — использование объекта БлокировкаДанных. Это позволяет нам явно указать системе, какие именно записи мы хотим заблокировать, не полагаясь на автоматические механизмы платформы.

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


// Создаем объект блокировки
Блокировка = Новый БлокировкаДанных;

// Добавляем элемент блокировки для регистра накопления
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");

// Устанавливаем режим блокировки: Исключительный (для записи) или Разделяемый (для чтения)
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;

// Указываем конкретные измерения, по которым будет установлена блокировка
ЭлементБлокировки.УстановитьЗначение("Склад", СсылкаНаСклад);
ЭлементБлокировки.ИсточникДанных = СписокТоваров;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");

// Выполняем блокировку
Блокировка.Заблокировать();

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

Оптимизация запросов и порядка захвата ресурсов

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

  1. Начало транзакции: процедура ОбработкаПроведения уже выполняется в транзакции.
  2. Установка блокировок: сразу определяем все ресурсы (регистры), которые будут затронуты, и вызываем Блокировка.Заблокировать().
  3. Чтение данных: выполняем запросы для получения текущих остатков. Благодаря установленной ранее блокировке, мы получим актуальные данные, которые никто не изменит в процессе.
  4. Проверка бизнес-логики: проверяем, хватает ли остатков.
  5. Запись движений: формируем и записываем наборы записей.

Важный момент: всегда соблюдайте одинаковую последовательность обращения к таблицам во всех модулях системы. Если в одном документе сначала записывается "РегистрБухгалтерии", а потом "РегистрНакопления", то и во всех остальных документах порядок должен быть таким же.

Использование пакетных запросов для минимизации времени блокировки

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТЧ.Номенклатура,
    |   ТЧ.Количество
    |ПОМЕСТИТЬ ВтТовары
    |ИЗ
    |   &ТЧ КАК ТЧ
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   Остатки.Номенклатура,
    |   Остатки.КоличествоОстаток
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки(
    |       &МоментВремени,
    |       Номенклатура В (ВЫБРАТЬ ВтТовары.Номенклатура ИЗ ВтТовары) И Склад = &Склад) КАК Остатки";

Запрос.УстановитьПараметр("ТЧ", Объект.Товары.Выгрузить());
Запрос.УстановитьПараметр("Склад", Объект.Склад);
Запрос.УстановитьПараметр("МоментВремени", Новый Граница(Объект.МоментВремени(), ВидГраницы.Исключая));

// Результат запроса получаем максимально быстро
РезультатЗапроса = Запрос.ВыполнитьПакет();

В данном случае мы используем Временные таблицы (ПОМЕСТИТЬ), что позволяет СУБД оптимально построить план запроса. Чем быстрее отработает запрос, тем быстрее освободятся ресурсы для других пользователей.

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

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

Рассмотрим структуру файла настроек logcfg.xml, который поможет выявить виновника блокировки:


<config xmlns="http://v8.1c.ru/7.4/config/logcfg">
  <log location="C:\LOGS\LOCKS" history="24">
    <event>
      <eq property="Name" value="TLOCK"/>
    </event>
    <event>
      <eq property="Name" value="TDEADLOCK"/>
    </event>
    <property name="all"/>
  </log>
</config>

После сбора логов мы сможем увидеть, какой именно процесс (Context) удерживал блокировку и какой запрос вызвал ожидание. Это позволит нам точечно оптимизировать "узкие места" в коде 1С.

Практические советы по работе с транзакциями

В завершение нашего разбора сформулируем несколько золотых правил, которые помогут избежать конфликтов блокировок:

Мы проанализировали основные аспекты работы с блокировками в 1С 8.3. Применяя объект БлокировкаДанных, оптимизируя запросы и следуя правилам работы с транзакциями, вы сможете значительно повысить стабильность и скорость работы вашей системы в многопользовательском режиме.

← На главную