Разберем классическую ситуацию, с которой сталкиваются многие разработчики при массовой обработке данных: почему при программном обходе выборки документов вызов метода УстановитьПометкуУдаления(Истина) стабильно завершается ошибкой "Конфликт блокировок при выполнении транзакции", в то время как аналогичное интерактивное действие в форме списка над той же «пачкой» документов проходит успешно. Проанализируем причины этого явления и рассмотрим эффективные способы решения проблемы.
Выясним причину, по которой программный код ведет себя иначе, чем интерфейс. Когда мы запускаем цикл по выборке, платформа и СУБД ведут себя максимально агрессивно. Рассмотрим основные факторы:
Deadlock (взаимоблокировке).Рассмотрим вариант, предложенный опытными разработчиками: предварительную блокировку данных. Если мы заранее сообщим системе, что собираемся изменять эти объекты, менеджер блокировок 1С выстроит очередь доступа более корректно. Для этого в тексте запроса, который формирует выборку, следует использовать конструкцию ДЛЯ ИЗМЕНЕНИЯ.
Проанализируем пример кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|ДЛЯ ИЗМЕНЕНИЯ"; // Блокируем выбранные объекты для текущей транзакции
Выборка = Запрос.Выполнить().Выбрать();
Важный нюанс: Использование ДЛЯ ИЗМЕНЕНИЯ эффективно только внутри явной транзакции. Однако стоит помнить, что удержание большой транзакции может парализовать работу других пользователей. Поэтому этот метод лучше комбинировать с пакетной обработкой.
Посмотрим на ситуацию со стороны оптимизации бизнес-логики. Метод УстановитьПометкуУдаления неявно вызывает отмену проведения и перезапись объекта. При этом запускаются все подписки на события, проверки в модулях объекта (ПередЗаписью, ПриЗаписи) и пересчеты. Если мы уверены в корректности данных, мы можем существенно ускорить процесс, установив флаг ОбменДанными.Загрузка.
Рассмотрим реализацию:
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Отключаем лишние проверки и выполнение тяжелого кода в модулях
ДокОбъект.ОбменДанными.Загрузка = Истина;
// Платформа выполнит пометку удаления и очистку движений в упрощенном режиме
ДокОбъект.УстановитьПометкуУдаления(Истина);
КонецЦикла;
Этот подход сокращает время удержания транзакции, так как СУБД тратит меньше времени на выполнение кода 1С, сосредоточившись только на физическом удалении записей из таблиц регистра.
Выясним, как избежать эскалации блокировок. Если документов очень много, не стоит обрабатывать их все в одном цикле «на вылет». Правильнее будет разбивать обработку на порции (пакеты) и делать небольшие технологические паузы.
Разберем алгоритм по шагам:
Пример реализации с «микропаузой»:
Счетчик = 0;
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
ДокОбъект.УстановитьПометкуУдаления(Истина);
Счетчик = Счетчик + 1;
// Через каждые 50 документов даем системе "выдохнуть"
Если Счетчик % 50 = 0 Тогда
#Если Клиент Тогда
// В новых версиях платформы можно использовать Ожидать() или аналоги
// Для сервера можно использовать вызов фонового задания или простой цикл ожидания
#КонецЕсли
КонецЕсли;
КонецЦикла;
Если задача стоит в максимально быстрой очистке базы, иногда эффективнее сначала программно очистить наборы записей регистров, а затем просто пометить документ на удаление. Это разделяет одну тяжелую транзакцию отмены проведения на несколько более легких операций.
Проанализируем этот подход:
Вместо стандартного механизма, мы можем для каждого регистра, по которому делает движения документ, выполнить следующий код:
Набор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(Выборка.Ссылка);
Набор.Записать(); // Записываем пустой набор (очищаем движения)
После очистки всех движений, вызов УстановитьПометкуУдаления(Истина) пройдет мгновенно, так как системе не нужно будет анализировать таблицы итогов и удалять тысячи записей — они уже удалены точечно.
Мы проанализировали ситуацию и выяснили, что основной причиной конфликтов является высокая плотность транзакций в программном цикле. Интерактивная работа в форме списка более «щадящая» для СУБД из-за внутренних механизмов платформы, которые могут группировать запросы или выполнять их в асинхронном режиме.
Для решения проблемы в коде рекомендуется:
ОбменДанными.Загрузка = Истина, если бизнес-логика позволяет пропустить проверки.