Как работают блокировки в 1С при многопоточной записи и как избежать конфликтов доступа?

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

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

Различия блокировок в файловом и клиент-серверном режимах

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

  1. Файловый режим: Здесь блокировка устанавливается на уровне всей таблицы. Если один пользователь или фоновое задание записывает данные в справочник или регистр, вся таблица становится недоступной для записи другим сессиям. Это основное ограничение, препятствующее эффективной многопоточности в файловых базах.
  2. Клиент-серверный режим: В этом режиме блокировки устанавливаются на уровне записей (строк таблицы СУБД). Это позволяет разным потокам одновременно записывать данные в одну и ту же таблицу, если наборы изменяемых данных не пересекаются по ключевым полям (измерениям или уникальным идентификаторам).

Таким образом, переход на клиент-серверный вариант работы — это первый и обязательный шаг для реализации параллельной обработки данных.

Управляемые блокировки и режим RCSI

Разберем понятие управляемых блокировок. В современных конфигурациях 1С используется преимущественно управляемый режим блокировок. В этом режиме платформа берет на себя ответственность за логическую целостность данных, а СУБД (например, MS SQL Server) работает в режиме Read Committed Snapshot Isolation (RCSI).

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

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


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

Важно понимать: управляемая блокировка существует только в памяти менеджера блокировок кластера 1С. СУБД о ней ничего не знает до момента реальной попытки записи в таблицу.

Эскалация блокировок: подводные камни

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

Эскалация — это процесс автоматического укрупнения блокировки. Она бывает двух видов:

  1. Эскалация в 1С: Если в рамках одной транзакции устанавливаются управляемые блокировки на большое количество объектов (более 100 000 для версии 8.3), менеджер блокировок 1С может принять решение заблокировать всё пространство (всю таблицу) целиком.
  2. Эскалация в SQL Server: Если транзакция удерживает более 5 000 блокировок на уровне строк в одной таблице, SQL Server заменяет их одной блокировкой на уровне таблицы (TAB lock). Это мгновенно останавливает работу всех остальных потоков, пытающихся писать в эту же таблицу.

Для предотвращения эскалации при массовой загрузке следует записывать данные порциями (пакетами). Рассмотрим пример оптимальной структуры цикла загрузки:


// Загрузка порциями по 1000 строк для предотвращения эскалации
Для Каждого ПорцияИзДанных Из ОбщийМассивДанных Цикл
    НачатьТранзакцию();
    Попытка
        // Обработка и запись данных
        ЗаписатьНаборДанных(ПорцияИзДанных);
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        // Логирование ошибки
    КонецПопытки;
КонецЦикла;

Разделитель итогов (Splitter)

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

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

Стратегия многопоточной загрузки без Deadlocks

Рассмотрим рекомендации по реализации задачи автора (загрузка 10 млн строк через фоновые задания):

  1. Сортировка ресурсов: Чтобы избежать взаимоблокировок (Deadlocks), все потоки должны захватывать ресурсы в одинаковом порядке. Например, если ваши документы делают движения по нескольким регистрам, убедитесь, что запись в них всегда идет в алфавитном порядке имен таблиц.
  2. Уникальность данных: Если данные в файлах гарантированно не пересекаются по измерениям регистров, конфликтов на уровне записей быть не должно (при условии включенного разделения итогов).
  3. Размер транзакции: Не пытайтесь загрузить весь файл в одной транзакции. Оптимальный размер — от 1000 до 5000 строк. Это обеспечит баланс между скоростью записи и риском эскалации блокировок.
  4. Отключение итогов: Если загрузка выполняется в монопольном режиме или ночью, рассмотрите возможность временного отключения использования итогов через РегистрыНакопления.НужныйРегистр.УстановитьИспользованиеИтогов(Ложь). Это многократно ускорит запись, так как системе не придется тратить ресурсы на обновление агрегатов. После загрузки итоги нужно включить и пересчитать.

Итоги

Мы выяснили, что в клиент-серверном режиме 1С многопоточная запись вполне достижима. Главное — следить за границами транзакций, избегать порогов эскалации (100 000 объектов в 1С и 5 000 в SQL) и использовать встроенные механизмы платформы, такие как разделение итогов и управляемые блокировки данных. Соблюдение этих правил позволит сократить время обработки огромных массивов данных с нескольких дней до нескольких часов.

← На главную