Как решить проблему нехватки памяти в объемных отчетах СКД на основе внешних источников данных?

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

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

В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим проверенные методы оптимизации, которые позволят вывести «мега-отчеты» без краха системы.

Почему СКД «съедает» память при работе с Таблицей Значений?

Для начала проанализируем внутренние механизмы работы платформы. Когда мы используем Набор данных - запрос, СКД транслирует инструкции напрямую в СУБД (SQL Server, PostgreSQL), и основная нагрузка по фильтрации и агрегации ложится на плечи сервера базы данных. В случае же с Набор данных - объект (наша ТаблицаЗначений), ситуация иная:

  1. Дублирование данных: Память сначала занимает сама исходная таблица значений. Затем, при передаче в СКД, система создает внутреннюю копию этих данных для выполнения операций группировки, сортировки и расчета итогов. В итоге потребление оперативной памяти возрастает в 2–3 раза относительно объема исходной таблицы.
  2. Ограничения 32-битной архитектуры: Если сервер 1С или клиентское приложение работают в 32-битном режиме, предел адресуемой памяти для одного процесса составляет около 2–4 ГБ. Даже если на сервере установлено 128 ГБ RAM, процесс rphost просто «упрется в потолок» и выдаст ошибку.
  3. Этап вывода: Ошибка часто возникает не в момент формирования данных, а в момент их визуализации в ТабличныйДокумент, когда система пытается построить в памяти гигантскую «сетку» отчета со всеми расшифровками и оформлением.

Рассмотрим пошагово способы решения этой проблемы.

Способ 1. Переход на 64-битную платформу

Это самое прямолинейное и эффективное решение. Как показывает практика, переход на 64-битный сервер и клиентское приложение снимает жесткое ограничение на объем используемой памяти. В этом случае процесс сможет использовать всю доступную оперативную память сервера. Однако стоит помнить, что это не отменяет проблему скорости: отчет может формироваться долго, но он хотя бы перестанет завершаться аварийно.

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

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

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


// В модуле объекта отчета или в обработке вывода
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
// Четвертый параметр "Истина" позволяет использовать внешние функции, 
// но если мы передаем "Неопределено" в ДанныеРасшифровки, 
// это существенно сэкономит память.

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

Как отмечают разработчики на форумах, отключение расшифровки может дать прирост производительности и снижение потребления памяти на 15–20%.

Способ 3. Потоковый вывод результата

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

Разберем пример такого вывода:


ПроцессорВывода.НачатьВывод();
Пока Истина Цикл
    ЭлементРезультата = ПроцессорКомпоновки.Следующий();
    Если ЭлементРезультата = Неопределено Тогда
        Прервать;
    КонецЕсли;
    ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
    
    // Здесь можно реализовать проверку потребляемой памяти
    // или периодическую фиксацию промежуточных итогов
КонецЦикла;
ПроцессорВывода.ЗакончитьВывод();

Способ 4. Кэширование данных в физические таблицы (Регистры)

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

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

  1. Регламентным заданием или по кнопке «Обновить данные» опрашиваем веб-сервисы.
  2. Очищаем регистр и записываем в него полученные данные.
  3. В СКД вместо Набора данных - объект используем обычный Набор данных - запрос к этому регистру.

Это дает колоссальное преимущество: СКД будет использовать всю мощь SQL-запросов, а фильтрация данных (отборы пользователей) будет происходить на уровне СУБД, а не в оперативной памяти 1С.

Способ 5. Использование Менеджера временных таблиц (ТЗ во времянку)

Существует продвинутый способ «обмануть» СКД, заставив ее работать с ТаблицейЗначений как с таблицей SQL. Мы можем поместить нашу ТЗ во временную таблицу и передать менеджер временных таблиц в запрос СКД.

Рассмотрим, как это реализовать программно:


// 1. Создаем менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// 2. Помещаем ТЗ во временную таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ";
Запрос.УстановитьПараметр("ТЗ", ИсходнаяТаблицаЗначений);
Запрос.Выполнить();

// 3. Передаем менеджер в настройки СКД
Настройки = Отчет.КомпоновщикНастроек.ПолучитьНастройки();
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;

// В схеме СКД должен быть "Набор данных - запрос", обращающийся к ВТ_Данные
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// Передаем МенеджерВТ третьим параметром
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
// Важно: в самом макете СКД нужно программно пробросить менеджер ВТ в процессор

Этот метод позволяет избежать дублирования данных в памяти 1С, так как основная работа по группировке переносится в tempdb на стороне SQL-сервера.

Способ 6. Прямой экспорт в Excel (минуя Табличный Документ)

Если цель пользователя — просто получить файл для дальнейшего анализа, стоит рассмотреть вариант выгрузки данных напрямую в формат .xlsx. Объект ТабличныйДокумент в 1С потребляет очень много памяти при отображении сотен тысяч строк. Использование сторонних библиотек или COM-объектов для прямой записи в Excel позволит сформировать файл гораздо быстрее и с меньшими затратами ресурсов.

Выясним причину, по которой пользователи запрашивают такие отчеты. Часто оказывается, что им нужен не весь отчет, а лишь возможность фильтровать его. В таком случае лучшим решением будет обучение пользователей работе с отборами СКД на стороне сервера, чтобы в результат попадало не более 5-10 тысяч строк одновременно.

Резюме

Работа с большими внешними данными в СКД требует осторожности. Для стабильной работы рекомендуем:

Соблюдение этих правил позволит вашим отчетам работать стабильно даже при обработке миллионов строк из внешних источников.

← На главную