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

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

При необходимости обработать массив документов (например, при переносе данных, закрытии периода или массовой загрузке) разработчики нередко сталкиваются с ситуацией, когда скорость выполнения операции кажется недостаточной, а показатели загрузки серверного оборудования (CPU, RAM, сеть) остаются на уровне 10–15%. Разберем архитектурные причины такого поведения платформы 1С:Предприятие и рассмотрим конкретные технические способы многократного ускорения массовой записи и проведения документов.

Почему один сеанс 1С не загружает серверное железо

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

  1. Загрузка одного ядра процессора: Если на многопроцессорном сервере с 16 или 32 ядрами один рабочий процесс rphost загружает одно ядро на 100%, суммарный мониторинг операционной системы покажет общую загрузку CPU всего в несколько процентов. Платформа 1С не умеет автоматически распараллеливать линейный код проведения одного документа на несколько ядер.
  2. Сетевые задержки (RTT — Round Trip Time): При проведении одного документа сервер приложений 1С выполняет десятки и сотни отдельных мелких запросов к СУБД (проверка ссылок, блокировки, чтение остатков, запись табличных частей, пересчет агрегатов итогов). Даже при минимальной сетевой задержке между сервером 1С и SQL-сервером в 0.3–0.5 мс суммарное ожидание сетевых пакетов физически ограничивает производительность одной очереди до 50–150 документов в секунду.
  3. Ожидания ввода-вывода (IO) при фиксации транзакций: При записи каждого отдельного документа СУБД выполняет синхронную фиксацию в журнал транзакций (Transaction Log), что упирается в скорость отклика дисковой подсистемы.

Оптимизация работы с транзакциями

По умолчанию каждый вызов метода Записать() в 1С неявно открывает и фиксирует отдельную транзакцию базы данных. Это приводит к колоссальным накладным расходам на дисковые операции fsync и запись страниц лога. Объединим запись документов в явные пакетные транзакции.

Рассмотрим пример пакетной обработки порциями:


РазмерПорции = 100;
Счетчик = 0;

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

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

Важное предостережение: Не следует помещать всю выборку (например, 100 000 документов) в одну гигантскую транзакцию. Слишком длинная транзакция приводит к лавинообразному разрастанию журнала транзакций СУБД, захвату эскалированных блокировок и исчерпанию оперативной памяти. Оптимальный размер порции обычно составляет от 50 до 200 документов.

Управление итогами регистров накопления и бухгалтерии

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

Применим программное отключение пересчета итогов на время пакетной операции:


// Отключаем использование итогов перед массовым проведением
РегистрыНакопления.ТоварыНаСкладах.УстановитьИспользованиеИтогов(Ложь);
РегистрыНакопления.ВзаиморасчетыСКонтрагентами.УстановитьИспользованиеИтогов(Ложь);

Попытка
    // Выполняем массовое проведение документов
    ВыполнитьМассовоеПроведение();
    
    // Включаем итоги обратно и пересчитываем их затронутый период
    РегистрыНакопления.ТоварыНаСкладах.УстановитьИспользованиеИтогов(Истина);
    РегистрыНакопления.ВзаиморасчетыСКонтрагентами.УстановитьИспользованиеИтогов(Истина);
    
    РегистрыНакопления.ТоварыНаСкладах.ПересчитатьИтоги();
    РегистрыНакопления.ВзаиморасчетыСКонтрагентами.ПересчитатьИтоги();
Исключение
    РегистрыНакопления.ТоварыНаСкладах.УстановитьИспользованиеИтогов(Истина);
    РегистрыНакопления.ВзаиморасчетыСКонтрагентами.УстановитьИспользованиеИтогов(Истина);
    ВызватьИсключение;
КонецПопытки;

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

Распараллеливание нагрузки через фоновые задания

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

Разберем алгоритм распараллеливания по шагам:

  1. Разделение данных: Разобьем массив документов на непересекающиеся группы (по организациям, складам, диапазонам номеров или хешу ссылок), чтобы параллельные потоки не блокировали одни и те же строки регистров.
  2. Запуск потоков: Инициализируем фоновые задания с помощью менеджера фоновых заданий ФоновыеЗадания.Выполнить(). Количество потоков обычно выбирается равным количеству свободных физических ядер сервера (от 4 до 16).
  3. Включение разделения итогов: В свойствах регистров конфигурации должно быть включено свойство Разрешить разделение итогов, что позволит разным потокам параллельно записывать движения без монопольной блокировки таблиц агрегатов.

МассивЗаданий = Новый Массив;

Для НомерПотока = 1 По КоличествоПотоков Цикл
    ПараметрыЗадания = Новый Массив;
    ПараметрыЗадания.Добавить(СтруктураПараметровПотока);
    
    Задание = ФоновыеЗадания.Выполнить("МодульПроведения.ПараллельноеПроведениеПотока", 
                                      ПараметрыЗадания, 
                                      , 
                                      "Проведение_Поток_" + Строка(НомерПотока));
    МассивЗаданий.Добавить(Задание);
КонецЦикла;

// Ожидаем завершения всех параллельных потоков
ФоновыеЗадания.ОжидатьЗавершения(МассивЗаданий);

Отключение служебных механизмов и валидаций

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


ДокументОбъект = Ссылка.ПолучитьОбъект();
ДокументОбъект.ОбменДанными.Загрузка = Истина;
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

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

Дополнительно рекомендуется временно отключить следующие подсистемы на время пакетной операции:

  1. Регистрация событий: Понизить уровень детализации журнала регистрации до фиксации только ошибок.
  2. Полнотекстовый поиск: Отключить регламентное задание обновления индекса ППД или временно снять признак использования полнотекстового поиска у метаданных.
  3. История данных: Приостановить версионирование объектов подсистемы БСП.

Настройка СУБД и инфраструктуры

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

  1. Размещение на одном хосте (Shared Memory / Unix Domain Sockets): Если сервер «1С:Предприятия» и СУБД размещены на одной физической машине, подключение через протокол Shared Memory (для MS SQL Server) или локальные сокеты (для PostgreSQL) устраняет сетевой оверхед и в разы снижает задержки между запросами.
  2. Отложенная фиксация транзакций (Delayed Durability): Для MS SQL Server включение параметра базы данных DELAYED_DURABILITY = FORCED позволяет подтверждать фиксацию транзакций в оперативной памяти до физического сброса на диск, что многократно увеличивает темп коротких записей. Для PostgreSQL аналогичный эффект дает параметр synchronous_commit = off.
  3. Параллелизм запросов (MAXDOP): Установим параметр MAXDOP = 1 на уровне СУБД. Попытки SQL-сервера распараллелить мелкие запросы 1С на несколько ядер приводят к накладным расходам на синхронизацию потоков и падению производительности.
← На главную