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

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

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

Почему возникают дедлоки при удалении

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

  1. Взаимосвязанные объекты: Электронные письма часто связаны через реквизиты "Основание" или "В ответ на". Если один поток удаляет родительское письмо, а второй — дочернее, их транзакции пересекаются при проверке одних и тех же ссылок.
  2. Индексы и регистры: Удаление объекта инициирует очистку движений и записей в зависимых регистрах. Если несколько потоков одновременно модифицируют один и тот же индексный диапазон таблицы СУБД, возникает блокировка.
  3. Эскалация блокировок: При большом количестве удаляемых строк СУБД (например, MS SQL Server) может заменить блокировки отдельных строк на блокировку всей таблицы, что мгновенно останавливает остальные потоки.

Решение 1: Принудительные управляемые блокировки

Для того чтобы система не "зависала" на ожидании, мы можем явно указать платформе, какой объект мы собираемся модифицировать. Использование исключительной блокировки позволяет другим потокам сразу понять, что объект занят, и не пытаться его обрабатывать. Проанализируем пример кода для фонового задания:


Попытка
    НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
    
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("Документ.ЭлектронноеПисьмоВходящее");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    ЭлементБлокировки.УстановитьЗначение("Ссылка", ТекущаяСсылка);
    Блокировка.Заблокировать();
    
    // Получаем объект и удаляем его
    ДокументОбъект = ТекущаяСсылка.ПолучитьОбъект();
    Если ДокументОбъект <> Неопределено Тогда
        ДокументОбъект.Удалить();
    КонецЕсли;
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    // Логируем пропуск объекта или добавляем в очередь на повтор
КонецПопытки;

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

Решение 2: Обработка иерархических цепочек (Транзитивное замыкание)

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


ВЫБРАТЬ
    ВзаимодействиеОснование КАК НачалоДуги,
    Ссылка КАК КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины1
ИЗ
    Документ.ЭлектронноеПисьмоВходящее
ГДЕ
    ВзаимодействиеОснование <> ЗНАЧЕНИЕ(Документ.ЭлектронноеПисьмоВходящее.ПустаяСсылка);

// Далее через соединения таблицы самой с собой получаем связи на глубину 2, 4, 8 и т.д.
// Это позволяет выявить всю цепочку переписки и назначить ее целиком одному потоку.

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

Решение 3: Очистка зависимых регистров

Часто конфликт возникает не в самой таблице документов, а в независимых регистрах сведений (например, версионирование или связи объектов CRM). Если мы удаляем объект, платформа проверяет эти регистры. Попробуем оптимизировать этот процесс, очищая регистры явно перед удалением документа:


НаборЗаписей = РегистрыСведений.СвязиОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(ТекущаяСсылка);
НаборЗаписей.Записать(); // Очищаем без замещения

// После этого удаление документа пройдет гораздо "легче"
ДокументОбъект.Удалить();

Совет: Внутри транзакции также стоит устанавливать блокировку на набор записей регистра, используя измерения отбора.

Решение 4: Групповое удаление через УдалитьОбъекты

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


МассивСсылок = Новый Массив;
// Заполняем массив порцией данных (например, по 100-500 штук)
УдалитьОбъекты(МассивСсылок, Истина); // Истина - проверять ссылки

Практические рекомендации по настройке потоков

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

  1. Разделение порций по GUID: Вместо разделения по датам, которые могут пересекаться в цепочках писем, попробуйте делить данные по остатку от деления числового представления уникального идентификатора (GUID). Это обеспечит равномерную нагрузку и физическое разделение данных в таблицах SQL.
  2. Ограничение нагрузки: Не запускайте слишком много потоков. На 4-ядерном сервере оптимально использовать 2-3 потока, чтобы оставить ресурсы для работы пользователей и системных процессов.
  3. Режим загрузки: Перед удалением объекта устанавливайте свойство ДокументОбъект.ОбменДанными.Загрузка = Истина;. Это отключит прикладные проверки в модулях объекта (но не проверку ссылочной целостности платформой), что ускорит процесс.
  4. Обработка "хвостов": Всегда будут объекты, которые не удалось удалить из-за временных блокировок. Предусмотрите механизм "повторного прохода" — собирайте ссылки, на которых вылетело исключение, и обрабатывайте их в конце в один поток.

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

← На главную