Как быстро удалить миллионы объектов из базы данных 1С?

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

Периодически каждый разработчик и администратор баз данных 1С сталкивается с необходимостью удаления колоссальных объемов информации. Это могут быть задачи свертки базы данных перед переходом на новую конфигурацию, разделение учета по организациям с выгрузкой в отдельные базы или просто плановая очистка устаревших и неактуальных сведений. Когда речь заходит об удалении сотен тысяч или миллионов элементов (например, справочника номенклатуры вместе с подчиненными единицами измерения, штрихкодами, ценами и регистрами сведений), стандартные механизмы платформы начинают работать неприемлемо медленно. Штатная обработка «Удаление помеченных объектов» в таких сценариях может выполняться сутками, парализуя работу пользователей.

В этой статье мы подробно разберем, почему стандартное удаление происходит так медленно, проанализируем физику процесса на уровне СУБД и рассмотрим эффективные практические методы оптимизации, которые позволят сократить время удаления миллионов объектов с нескольких суток до нескольких часов.

Почему стандартное удаление в 1С выполняется медленно?

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

  1. Пообъектный подход платформы: Платформа 1С:Предприятие исторически ориентирована на объектную технику. Даже при массовом удалении каждый объект обрабатывается индивидуально. Для каждого элемента СУБД выполняет отдельный запрос DELETE.
  2. Контроль ссылочной целостности: Перед удалением система обязана проверить, не ссылается ли какой-то другой объект в базе данных на удаляемый элемент. Для этого платформа сканирует все таблицы базы данных, где теоретически может содержаться ссылка на данный объект. При миллионных объемах этот процесс генерирует колоссальное количество чтений.
  3. Обновление индексов СУБД: Справочники с миллионами записей всегда имеют множество индексов (по коду, наименованию, реквизитам). При удалении каждой записи СУБД вынуждена на лету перестраивать эти индексы. Если удаление идет по одному объекту, СУБД тратит огромные ресурсы на постоянную микро-модификацию индексных страниц.
  4. Запись в журнал регистрации и транзакционный лог СУБД: Каждое удаление фиксируется платформой в журнале регистрации 1С, а СУБД записывает изменения в свой транзакционный лог (LDF-файл для MS SQL). Это создает высокую нагрузку на дисковую подсистему (I/O).

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

Решение 1. Программное удаление порциями в транзакциях

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


// НЕПРАВИЛЬНО: Медленное удаление из выборки
Выборка = Справочники.ЕдиницыИзмерения.Выбрать();
Пока Выборка.Следующий() Цикл
    Выборка.Ссылка.ПолучитьОбъект().Удалить();
КонецЦикла;

Этот код будет работать крайне медленно, так как при удалении объекта выборка может «сбиваться», а СУБД будет открывать и закрывать транзакцию для каждого отдельного элемента. Чтобы ускорить процесс в десятки раз, мы должны применить два важнейших правила: предварительное получение массива ссылок и группировку операций в транзакции.

Рассмотрим правильный шаблон реализации пакетного программного удаления:


// ПРАВИЛЬНО: Пакетное удаление в транзакциях
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЕдиницыИзмерения.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения";

ТаблицаДанных = Запрос.Выполнить().Выгрузить();
МассивСсылок = ТаблицаДанных.ВыгрузитьКолонку("Ссылка");

ВсегоОбъектов = МассивСсылок.Количество();
РазмерПорции = 1000; // Оптимальный размер порции для простых справочников

Индекс = 0;
Пока Индекс < ВсегоОбъектов Цикл
    НачатьТранзакцию();
    Попытка
        Для К = 1 По РазмерПорции Цикл
            ТекущийИндекс = Индекс + К - 1;
            Если ТекущийИндекс >= ВсегоОбъектов Тогда
                Прервать;
            КонецЕсли;
            
            СсылкаНаОбъект = МассивСсылок[ТекущийИндекс];
            // Получаем объект и удаляем его непосредственно без проверок
            Объект = СсылкаНаОбъект.ПолучитьОбъект();
            Если Объект <> Неопределено Тогда
                Объект.ОбменДанными.Загрузка = Истина; // Отключаем обработчики событий
                Объект.Удалить();
            КонецЕсли;
        КонецЦикла;
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписатьЛогОшибки(ОписаниеОшибки());
    КонецПопытки;
    
    Индекс = Индекс + РазмерПорции;
    
    // Выводим состояние не на каждой итерации, а порционно!
    Если Индекс % 10000 = 0 Тогда
        Состояние("Удалено объектов: " + Индекс + " из " + ВсегоОбъектов);
    КонецЕсли;
КонецЦикла;

Разберем, почему этот код работает в разы быстрее:

Решение 2. Избавление от «скрытых» убийц производительности (Состояние и Сообщить)

Проанализируем частую ошибку оптимизации интерфейса. Разработчики часто вставляют в цикл удаления информационные сообщения для пользователя, например: Состояние("Удаление объекта " + Номер) на каждой итерации цикла. Замеры производительности показывают, что при удалении простых объектов (таких как штрихкоды или единицы измерения) на отрисовку прогресса и передачу данных между сервером и клиентом может уходить до 75% всего времени работы алгоритма!

Если вам необходимо выводить прогресс на экран, делайте это дискретно — например, раз в 5 000 или 10 000 удаленных элементов, используя оператор остатка от деления: Если Индекс % 5000 = 0 Тогда. А еще лучше — выполняйте удаление в фоновом задании на сервере без интерактивного взаимодействия с клиентом.

Решение 3. Очистка на уровне СУБД (для клиент-серверных баз)

Если ваша база данных работает под управлением MS SQL Server или PostgreSQL, а удаляемые объекты гарантированно не имеют внешних связей (или вы сознательно берете на себя ответственность за ссылочную целостность), самым быстрым способом будет прямое удаление данных на уровне СУБД.

Для реализации этого метода выполним следующие шаги:

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

Пример прямого SQL-запроса для очистки данных:


-- Прямое удаление данных на уровне MS SQL Server
-- ВНИМАНИЕ: Обязательно сделайте резервную копию базы перед выполнением!

-- Очищаем подчиненные табличные части (если они есть)
DELETE FROM _Reference125_VT1234 WHERE _Reference125_IDRRef IN 
(SELECT _IDRRef FROM _Reference125 WHERE <Условие_на_удаление>);

-- Удаляем сами элементы справочника
DELETE FROM _Reference125 WHERE <Условие_на_удаление>;

Важное предупреждение: Использование прямых SQL-запросов для модификации данных нарушает лицензионное соглашение фирмы 1С и может привести к разрушению логической целостности базы данных. После выполнения таких операций на уровне СУБД в обязательном порядке запустите в 1С процедуру «Тестирование и исправление» (режим «Проверка ссылочной целостности -> Назначать ссылки/Очищать ссылки»).

Решение 4. Использование RAM-диска для файловых баз данных

Если ваша база данных файловая (формат 1CD) и перевести ее на SQL-версию нет технической возможности, главным узким местом при удалении миллионов записей будет скорость работы дисковой подсистемы. Запись логов транзакций файловой СУБД на медленный HDD или даже обычный SSD занимает много времени.

Мы можем временно обойти это аппаратное ограничение, используя RAM-диск (виртуальный диск, созданный в оперативной памяти компьютера):

  1. Установим утилиту для создания RAM-дисков (например, ImDisk или аналогичное ПО).
  2. Выделим достаточный объем оперативной памяти под виртуальный диск (размер диска должен быть больше размера файла 1CD с запасом под временные файлы).
  3. Скопируем файл базы данных 1Cv8.1CD на созданный RAM-диск.
  4. Запустим процесс программного удаления (используя пакетный метод из Решения 1). Скорость дисковых операций в оперативной памяти выше в сотни раз, что позволит выполнить чистку максимально быстро.
  5. После завершения удаления и обязательного сжатия базы данных перенесем файл 1Cv8.1CD обратно на постоянный накопитель.

Решение 5. Радикальный подход: Метод «Выгрузка только нужного»

Посмотрим на ситуацию с математической точки зрения. Если в вашем справочнике содержится 2.5 миллиона элементов, и вам нужно вычистить 2 миллиона из них, то эффективнее будет не удалять 80% базы данных, а сохранить оставшиеся 20%.

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

  1. Создадим новую чистую информационную базу с аналогичной конфигурацией.
  2. С помощью обработки «Универсальный обмен данными XML» или конфигурации «Конвертация данных» настроим перенос только тех объектов, которые должны остаться (например, актуальные документы за последний год, остатки и только используемая в них номенклатура).
  3. Перенесем эти данные в новую базу. При переносе платформа автоматически создаст только те элементы справочников, на которые есть ссылки в переносимых документах.
  4. В результате мы получим абсолютно чистую базу данных без зависших битых ссылок, неиспользуемых элементов номенклатуры и раздутых индексных таблиц.

Резюме и практические рекомендации

Удаление миллионов объектов из 1С — это комплексная задача, требующая понимания механизмов платформы и СУБД. Резюмируем ключевые правила, которые помогут вам сэкономить время:

← На главную