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

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

При программной обработке или перезаписи движений документов, формирующих тысячи проводок (например, регламентный документ ЗакрытиеМесяца), разработчики нередко сталкиваются с катастрофическим падением производительности. Ситуация, когда запись набора из 9 000–10 000 строк занимает несколько часов, а замер производительности показывает, что 99% времени уходит на метод НаборЗаписей.Записать(), встречается на практике довольно часто. Разберем причины такого поведения платформы и СУБД, а также детально рассмотрим способы оптимизации этой операции.

В чем причина медленной записи проводок

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

  1. Таблица основных движений (сами проводки со счетами, суммами и количеством).
  2. Таблицы субконто (отдельные физические таблицы значений субконто для дебета и кредита).
  3. Таблицы агрегированных итогов (остатки и обороты по счетам, а также аналитические срезы по комбинациям субконто за каждый месяц).

Когда мы вызываем метод Записать() для набора из 10 000 проводок, платформа не просто вставляет строки в одну таблицу. СУБД выполняет сотни тысяч вставок и обновлений связанных индексов. Более того, если дата документа находится в прошлом периоде, платформа 1С инициирует автоматический каскадный пересчет ежемесячных срезов итогов от даты документа до текущего момента времени.

В среде СУБД PostgreSQL проблема усугубляется механизмом очистки: при стандартной перезаписи платформа сначала удаляет старые записи по регистратору, а затем добавляет новые. Это порождает большое количество «мертвых строк» (dead tuples), раздувает индексы и заставляет планировщик запросов выбирать неоптимальные планы.

Рассмотрим по шагам основные способы решения данной задачи.

Решение 1. Временное отключение пересчета итогов регистра

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

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


// Отключаем расчет итогов для ускорения записи
РегистрыБухгалтерии.Хозрасчетный.УстановитьИспользованиеИтогов(Ложь);

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

При включении флага обратно платформа пересчитает только те итоги, которые были затронуты изменениями. Это занимает несоизмеримо меньше времени, чем пересчет «на лету» в момент построчной записи.

Решение 2. Отключение событий через ОбменДанными.Загрузка

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

Решение 3. Управление периодом рассчитанных итогов

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


ГраницаИтогов = НачалоМесяца(Док.Дата);
РегистрыБухгалтерии.Хозрасчетный.УстановитьПериодРассчитанныхИтогов(ГраницаИтогов);

// Выполняем чтение и запись набора записей
НаборЗаписей.Записать();

// После завершения пересчитываем итоги точечно за нужный период
РегистрыБухгалтерии.Хозрасчетный.ПересчитатьИтогиЗаПериод(НачалоМесяца(Док.Дата), КонецМесяца(Док.Дата));

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

Решение 4. Порционная запись данных без замещения

Если модифицируемый массив данных слишком велик и вызывает ошибки нехватки памяти (особенно в 32-разрядных процессах), разберем альтернативный алгоритм порционной перезаписи:

  1. Выгружаем прочитанные движения во временную таблицу значений и очищаем набор в памяти.
  2. Записываем пустой набор с замещением, тем самым очищая движения документа в базе данных: НаборЗаписей.Записать(Истина);
  3. Организуем цикл по порциям (например, по 500–1 000 строк), заполняя набор и записывая его с отключенным замещением: НаборЗаписей.Записать(Ложь);

Такой подход исключает длительные блокировки и позволяет СУБД фиксировать промежуточные транзакции меньшими порциями.

Рекомендации по обслуживанию СУБД PostgreSQL

Если на сервере регулярно проводятся массивные регламентные процедуры, обратим внимание на следующие настройки СУБД:

  1. Регулярный регламент вакуума: после объемных перезаписей выполните операцию VACUUM ANALYZE для таблиц движений и итогов регистра, чтобы удалить накопившиеся мертвые кортежи и обновить статистику для оптимизатора.
  2. Сжатие таблиц итогов: периодически запускайте стандартную процедуру «Сжатие таблиц информационной базы» в меню «Тестирование и исправление» 1С. Это объединяет строки буфера разделения итогов и существенно ускоряет последующие операции записи.
← На главную