При разработке механизмов загрузки больших объемов данных (например, из файлов Excel на 400 000 строк и более) перед разработчиком 1С встает критическая задача: как передать обработанную пользователем таблицу значений (ТЗ) в фоновое задание (ФЗ) максимально быстро и без риска падения сервера по памяти. Проблема осложняется тем, что фоновое задание запускается в отдельном сеансе, и прямая передача ссылок на объекты формы невозможна. Рассмотрим подробнее основные архитектурные подходы, разберем их плюсы и минусы, а также проанализируем ситуацию с точки зрения потребления ресурсов системы.
Это самый простой и очевидный способ. Мы подготавливаем таблицу на сервере (выгружаем ее из реквизита формы) и передаем как элемент массива параметров в метод ФоновыеЗадания.Выполнить(). Проанализируем этот процесс: при вызове метода платформа выполняет сериализацию ТЗ для передачи данных в новый сеанс фонового задания.
Важный нюанс: в момент сериализации объем занимаемой оперативной памяти может вырасти в 2–3 раза. Если ваша таблица занимает 500 МБ, то в пике серверу может потребоваться до 1.5 ГБ свободного пространства в рамках одного процесса rphost. На 32-битных серверах это гарантированно приведет к ошибке "Недостаточно памяти". Рассмотрим пример кода:
&НаСервере
Процедура ЗапуститьОбработкуНаСервере()
// Выгружаем данные из реквизита формы (коллекции ДанныеФормыКоллекция) в обычную ТЗ
ТаблицаДляФЗ = Объект.Товары.Выгрузить();
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ТаблицаДляФЗ);
// Запускаем фоновое задание
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ОбработатьДанные", ПараметрыЗадания);
КонецПроцедуры
Этот метод подходит для таблиц до 100 000 – 150 000 строк при условии, что в строках нет тяжелых двоичных данных или огромных текстовых полей. Как показала практика обсуждения, на 8 000 строк задержка составляет всего около 3 секунд, что вполне приемлемо.
Этот метод часто считается более "щадящим", но у него есть свои особенности. Мы помещаем таблицу во временное хранилище и передаем в фоновое задание только Адрес (строку). Однако стоит помнить, что фоновое задание не имеет доступа к временному хранилищу текущего сеанса пользователя по умолчанию, если адрес привязан к уникальному идентификатору формы.
Для корректной работы мы должны использовать Новый УникальныйИдентификатор при помещении в хранилище. Это сделает данные доступными для всех сеансов. Выясним причину, почему это может быть медленнее: запись во временное хранилище — это по сути запись на диск или в базу данных (в зависимости от настроек кластера), что создает дополнительную нагрузку на I/O.
Рассмотрим пример правильной реализации:
&НаСервере
Функция ПоместитьТЗВоВременноеХранилище()
ТЗ = РеквизитФормыВЗначение("Объект.Товары");
// Используем новый ГУИД, чтобы ФЗ могло прочитать данные
АдресХранилища = ПоместитьВоВременноеХранилище(ТЗ, Новый УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
&НаСервере
Процедура ЗапуститьФЗЧерезХранилище()
Адрес = ПоместитьТЗВоВременноеХранилище();
Параметры = Новый Массив;
Параметры.Добавить(Адрес);
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ОбработатьИзХранилища", Параметры);
КонецПроцедуры
Совет: Не забудьте в самом фоновом задании после извлечения данных вызвать УдалитьИзВременногоХранилища(Адрес), чтобы не забивать ресурсы сервера "брошенными" данными.
Для действительно больших объемов (от 400 000 строк и до миллионов) самым надежным способом будет использование вспомогательного Регистра Сведений (РС). Это позволяет избежать ограничений оперативной памяти и обеспечивает стабильность при сбоях. Мы записываем данные в РС с уникальным ключом задания, а фоновое задание порционно считывает их.
Рассмотрим преимущества этого подхода:
Однако помните о блокировках: при записи более 50 000 строк в одной транзакции может произойти эскалация блокировки на весь регистр, что помешает другим пользователям. Поэтому запись в РС также следует делать порциями.
Если необходимо передать данные максимально компактно, разберем по шагам метод сжатия. Таблица значений в формате XDTO (стандарт для 1С) избыточна. Мы можем перевести ее в JSON, сжать методом Сжать() и передать как ДвоичныеДанные.
Посмотрим на пример логики:
// На стороне формы:
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ТаблицаЗначений);
СтрокаJSON = ЗаписьJSON.Закрыть();
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки(СтрокаJSON);
СжатыеДанные = Сжать(ДвоичныеДанные);
// Передаем СжатыеДанные в ФЗ
Этот способ значительно экономит сетевой трафик между рабочими процессами кластера и объем занимаемой памяти при хранении параметров задания.
Проанализируем ситуацию с типизацией. Строгая типизация колонок таблицы значений критически важна. Если колонка имеет тип "Любая ссылка" или составной тип, 1С выделяет под каждую ячейку гораздо больше памяти для хранения описания типа. Всегда используйте ОписаниеТипов при программном создании таблиц.
Если пользователь работает с данными на форме, старайтесь не загружать все 400 000 строк в ДанныеФормыКоллекция. Рассмотрите возможность использования динамического списка с основной таблицей (если данные уже в БД) или постраничного вывода. Помните, что клиентское приложение (особенно веб-клиент) крайне чувствительно к объему данных в оперативной памяти.
Подводя итог: для средних объемов (до 100к строк) используйте параметры ФЗ или Временное хранилище. Для критических объемов (400к+) и гарантированной стабильности — переходите на работу через физические таблицы базы данных (Регистры Сведений) или прямое чтение исходного файла на стороне сервера, минуя передачу всей таблицы с клиента.