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

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

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

Почему удаление пустого набора записей выполняется так долго?

Рассмотрим стандартный вызов очистки регистра:


НаборЗаписей = РегистрыСведений.ОбъектыИнформационныхБаз.СоздатьНаборЗаписей();
НаборЗаписей.Записать();

При выполнении этого кода для таблицы объемом в несколько миллионов строк платформа 1С и СУБД выполняют огромный объем сопутствующих операций:

  1. Регистрация изменений в планах обмена (РИБ). Если регистр включен в состав хотя бы одного плана обмена с включенной авторегистрацией, платформа при удалении строк параллельно создает миллионы записей в служебных таблицах регистрации изменений (таблицы с префиксом _RecordChange). Часто удаление уже завершено на уровне основной таблицы, но сеанс продолжает ожидать окончания записи изменений для обмена.
  2. Единая неделимая транзакция. Удаление без отборов выполняется в рамках одной монолитной транзакции СУБД. Это приводит к разрастанию журнала транзакций (LDF в MS SQL Server или WAL в PostgreSQL), блокировкам уровня всей таблицы и риску исчерпания дискового пространства.
  3. Построчное обновление индексов. Сервер СУБД вынужден модифицировать дерево каждого индекса таблицы, удаляя ссылки на каждую удаляемую строку, что генерирует колоссальную нагрузку на дисковую подсистему.

Способ 1. Отключение механизмов регистрации и событийных обработчиков

Если очистка регистра выполняется в рамках регламентных работ и передавать факт удаления в подчиненные узлы распределенной информационной базы (РИБ) не требуется, обязательно используем режим загрузки данных. Установка свойства ОбменДанными.Загрузка = Истина отключает платформенную авторегистрацию изменений и выполнение обработчиков ПередЗаписью и ПриЗаписи модуля набора записей.

Посмотрим на пример правильной записи пустого набора:


НаборЗаписей = РегистрыСведений.ОбъектыИнформационныхБаз.СоздатьНаборЗаписей();
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать();

Способ 2. Удаление данных порциями с фиксацией транзакций

Чтобы не блокировать таблицу целиком и не переполнять журнал транзакций СУБД, организуем удаление пакетами фиксированного размера. Для контроля процесса добавим вывод прогресса и метод ОбработкаПрерыванияПользователя(), позволяющий при необходимости прервать операцию комбинацией клавиш Ctrl + Break / Ctrl + C.

Разберем алгоритм порционной очистки с помощью запроса по ключевым полям:


РазмерПорции = 10000;
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерПорции, "ЧГ=0") + "
    |	Т.УзелОбмена,
    |	Т.Ссылка
    |ИЗ
    |	РегистрСведений.ОбъектыИнформационныхБаз КАК Т";

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

Способ 3. Очистка по срезам ведущих измерений или периодам

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

Способ 4. Прямая очистка на уровне СУБД (TRUNCATE TABLE)

Для мгновенной очистки таблицы на стороне СУБД можно использовать команду TRUNCATE TABLE. Этот метод занимает считанные секунды даже на таблицах в десятки миллионов строк, так как СУБД освобождает страницы данных целиком без построчного удаления.

При использовании прямого SQL-запроса важно учитывать следующие критические правила:

  1. Очистка таблиц регистрации изменений. Необходимо найти имя таблицы самого регистра (через метод ПолучитьСтруктуруХраненияБазыДанных()) и имя связанной таблицы изменений (префикс _RecordChange), после чего выполнить TRUNCATE TABLE для обеих таблиц. В противном случае в планах обмена останутся невалидные ссылки.
  2. Сброс кэша платформы 1С. После прямого вмешательства в базу данных на уровне СУБД обязательно требуется перезапустить сеансы пользователей или рабочий процесс кластера серверов 1С для актуализации серверного кэша.

Обязательные действия после массового удаления

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

  1. Обновление статистики СУБД. Массовое удаление делает старую статистику неактуальной, из-за чего оптимизатор СУБД начинает строить неоптимальные планы выполнения запросов.
  2. Реиндексация и дефрагментация. После очистки больших объемов данных страницы индексов фрагментируются. Рекомендуется запустить процедуру перестроения индексов (Rebuild Index / REINDEX).
  3. Усечение журнала транзакций. Если удаление происходило в одной транзакции, файл лога СУБД (LDF/WAL) может значительно увеличиться. При необходимости выполняем усечение (Shrink) журналов.
← На главную