Как передать большую таблицу значений (12 млн записей) в фоновое задание в 1С

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

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

Почему прямая передача ТЗ через параметры — это плохая идея

Проанализируем ситуацию: когда мы передаем таблицу значений в ФоновыеЗадания.Выполнить(), платформа инициирует процесс сериализации данных. ТЗ превращается в XML-поток (XDTO). Для таблицы в 12 миллионов строк объем XML-данных в памяти может в несколько раз превышать размер самих данных в базе. В 64-битных системах каждая строка ТЗ со служебной информацией занимает значительный объем RAM. В итоге, еще до начала выполнения кода в фоне, сервер тратит все ресурсы на упаковку объекта, что и приводит к потреблению 30 ГБ и более, как описано в проблеме автора.

Решение 1: Получение данных непосредственно в фоновом задании

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

Рассмотрим алгоритм действий:

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

Таким образом, мы полностью исключаем стадию сериализации гигантского объекта между сеансами.

Решение 2: Использование промежуточного регистра сведений

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

Разберем шаги реализации:

Важно: Запись в базу данных — операция не мгновенная, но она гораздо стабильнее работы с 30 ГБ оперативной памяти и позволяет избежать падения рабочего процесса rphost.

Решение 3: Передача через временный файл на сервере

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


// В основном сеансе:
ПутьКФайлу = ПолучитьИмяВременногоФайла("bin");
ЗаписьДанных = Новый ЗаписьДанных(ПутьКФайлу);
ЗаписатьРезультатСериализации(ЗаписьДанных, МояОгромнаяТЗ);
ЗаписьДанных.Закрыть();

ПараметрыФона = Новый Массив;
ПараметрыФона.Добавить(ПутьКФайлу);
ФоновыеЗадания.Выполнить("МойМодуль.ОбработатьФайлВФоне", ПараметрыФона);

В фоновом задании мы считываем файл. При использовании ДвоичныеДанные и сжатия (архивирования), передача информации происходит значительно эффективнее. Платформа не пытается анализировать структуру каждой из 12 млн строк при передаче пути к файлу.

Решение 4: Разбиение на порции (пакетирование) и параллелизм

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

Оптимальное количество потоков обычно составляет от 4 до 8, в зависимости от ядер процессора и нагрузки на дисковую подсистему (IOPS). Разберем пример логики разделения:


// Пример разделения таблицы на части
РазмерПакета = 1000000; // По 1 млн строк на поток
ВсегоСтрок = МояТЗ.Количество();
ТекущаяСтрока = 0;

Пока ТекущаяСтрока < ВсегоСтрок Цикл
    // Создаем подтаблицу или передаем границы индексов
    ПараметрыЗадания = Новый Структура;
    ПараметрыЗадания.Вставить("НачальныйИндекс", ТекущаяСтрока);
    ПараметрыЗадания.Вставить("Количество", РазмерПакета);
    
    ФоновыеЗадания.Выполнить("МойМодуль.ОбработатьЧастьДанных", ПараметрыЗадания);
    
    ТекущаяСтрока = ТекущаяСтрока + РазмерПакета;
КонецЦикла;

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

Механизм ожидания и синхронизации

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


МассивЗаданий = Новый Массив;
// ... запуск заданий и добавление их в массив ...
Попытка
    ФоновыеЗадания.ОжидатьЗавершения(МассивЗаданий, 3600); // Ожидание 1 час
    Сообщить("Все потоки завершены успешно");
Исключение
    Сообщить("Одно из заданий завершилось с ошибкой или превышено время ожидания");
КонецПопытки;

Подведем итоги

Для стабильной работы с таблицами объемом в 12 000 000 записей в 1С необходимо придерживаться следующих правил:

  1. Никогда не передавайте объект ТаблицаЗначений такого размера напрямую в параметры функции фонового задания.
  2. Используйте промежуточное хранилище: Регистр Сведений или файлы на диске сервера.
  3. Применяйте пакетирование: делите данные на порции по 500 тыс. — 1 млн строк.
  4. Контролируйте количество потоков: не запускайте более 10 фоновых заданий одновременно, чтобы не парализовать работу SQL-сервера.
  5. Вместо передачи данных старайтесь передавать алгоритм их получения внутри фона.

Соблюдение этих рекомендаций позволит вам эффективно использовать многопоточность в 1С без риска обрушить сервер из-за нехватки оперативной памяти.

← На главную