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

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

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

Анализ причин деградации производительности

Проанализируем механику работы платформы «1С:Предприятие» и СУБД при работе с регистром бухгалтерии. В отличие от простых регистров сведений, регистр бухгалтерии обладает сложной архитектурой:

  1. Пересчет итогов в реальном времени. По умолчанию при записи каждого набора записей платформа пересчитывает таблицы остатков и оборотов затронутых периодов. Чем больше данных уже присутствует в базе, тем более тяжелыми становятся эти вычисления.
  2. Сложная физическая структура таблиц. Регистр бухгалтерии представлен не одной таблицей, а набором связанных сущностей: таблицей движений, таблицей аналитики (значений субконто) и агрегатными таблицами итогов. Запись одной строки провоцирует серию вставок и проверок ссылочной целостности сразу в нескольких местах.
  3. Фрагментация индексов и расщепление страниц данных (Page Split). Кластерные индексы таблиц движений строятся по периоду и регистратору. Если новые записи добавляются не строго в конец таблицы, а распределяются по разным периодам, СУБД вынуждена разделять заполненные страницы данных (размером 8 КБ) на две. Это приводит к сильной фрагментации индексов и лавинообразному росту дисковых операций случайного ввода-вывода (Random I/O). Диск уходит в 100% занятости, ожидая завершения операций ASYNC_IO_COMPLETION.
  4. Перезапись наборов записей при объединении данных. Если порции проводок многократно добавляются в один и тот же документ-регистратор, вызов метода записи набора с признаком замещения по умолчанию удаляет все ранее внесенные проводки документа и вставляет весь массив заново, вызывая квадратичный рост накладных расходов.
  5. Накладные расходы отдельных транзакций. Запись каждого отдельного документа по умолчанию выполняется в собственной неявной транзакции, что вынуждает СУБД сбрасывать журнал транзакций на диск при каждой итерации.

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

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

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


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

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

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

Решение 2. Оптимизация записи и отключение сервисных механизмов платформы

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


// Отключение прикладных обработчиков и проверок
НаборЗаписей.ОбменДанными.Загрузка = Истина;
ДокументОбъект.ОбменДанными.Загрузка = Истина;

// Отключение регистрации изменений в планах обмена (РИБ)
НаборЗаписей.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
ДокументОбъект.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;

Также обратим внимание на архитектуру формирования документов-регистраторов:

  1. Не загружайте все проводки в один документ. Создание одного документа «Операция» на 200 000–300 000 строк перегружает оперативную память процесса сервера 1С при сериализации и блокирует регистр. Оптимальный размер одного документа-регистратора составляет от 2 000 до 10 000 проводок.
  2. Сортируйте исходные данные. Перед отправкой на запись массив проводок необходимо упорядочить по дате (периоду). Это позволит СУБД последовательно наполнять страницы индекса без их постоянного расщепления в середине B-дерева.

Решение 3. Группировка операций в явные транзакции

Запись каждого документа порождает отдельную дисковую операцию сброса лога транзакций СУБД (WRITELOG). Объединение записи нескольких документов в одну общую транзакцию снижает нагрузку на дисковую подсистему в десятки раз.

Разберем по шагам реализацию порционной фиксации транзакций:


Счетчик = 0;
РазмерПорции = 100; // Фиксируем по 100 документов в одной транзакции

НачатьТранзакцию();

Для Каждого ДанныеДокумента Из МассивЗагрузки Цикл
    
    // Создание и запись документа с движениями
    ЗаписатьДокументОперация(ДанныеДокумента);
    
    Счетчик = Счетчик + 1;
    
    Если Счетчик % РазмерПорции = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
    КонецЕсли;

КонецЦикла;

Если Счетчик % РазмерПорции <> 0 Тогда
    ЗафиксироватьТранзакцию();
КонецЕсли;

Решение 4. Обслуживание индексов и конфигурация СУБД

Если в процессе интенсивной вставки деградация скорости все же наступила, причиной является критическая фрагментация индексов таблицы движений и аналитики. Стандартные планы обслуживания могут пропускать таблицы, если статистика фрагментации еще не обновилась.

Для устранения задержек применимы следующие шаги:

  1. Дефрагментация и перестроение индексов. Выполните ручную реорганизацию или перестроение индексов (REBUILD INDEX) для таблиц движений регистра бухгалтерии (соответствующие таблицы в SQL имеют префикс _AccReg и _AccRegED). Это восстанавливает непрерывность страниц данных и снижает дисковые очереди.
  2. Включение мгновенной инициализации файлов (Instant File Initialization). Убедитесь, что сервисной учетной записи SQL Server выдано право «Выполнение задач по обслуживанию тома» (SE_MANAGE_VOLUME_NAME). Без этого при каждом автоувеличении файла базы данных (MDF) СУБД заполняет новое пространство нулями, полностью замораживая процесс записи.
  3. Упреждающее расширение файлов базы данных. Перед началом массовой загрузки вручную увеличьте размер файлов базы данных (MDF) и журнала транзакций (LDF) до предполагаемого итогового объема, а шаг автоприроста задайте фиксированным значением (например, 512 МБ), исключив автоприрост в процентах.
← На главную