Как загрузить 15 миллионов товаров из CSV файла в 1С без перегрузки оперативной памяти

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

Работа с экстремально большими объемами данных в 1С, такими как прайс-листы на 15 000 000 позиций объемом более 1 ГБ, требует особого подхода. Обычная попытка загрузить такой файл в объект ТаблицаЗначений неизбежно приведет к потреблению десятков гигабайт оперативной памяти и последующему аварийному завершению работы системы (ошибка Out of Memory или Ошибка формата потока). Разберем пошагово, как правильно организовать процесс импорта, фильтрации и синхронизации таких данных.

Причина проблемы: почему нельзя использовать Таблицу Значений целиком

Когда мы пытаемся прочитать файл в ТаблицуЗначений, платформа 1С резервирует память под каждую строку и каждую колонку. Для 15 миллионов строк, даже если в них только артикул и наименование, объем данных в оперативной памяти может вырасти в 4–10 раз относительно размера исходного CSV-файла. Как показала практика обсуждения, файл размером 1 ГБ легко «съедает» 27 ГБ оперативной памяти и вешает сервер. Единственный верный путь — потоковая обработка данных без загрузки всего массива в RAM.

Решение 1: Потоковое чтение файла через ФайловыйПоток

В современных версиях платформы 1С (8.3) реализован механизм потоковой работы с файлами, который позволяет читать данные построчно, не загружая файл целиком. Для этого используются объекты ФайловыйПоток и ЧтениеТекста. Это позволяет обрабатывать файлы любого размера, ограничиваясь лишь скоростью дисковой подсистемы и процессора.

Рассмотрим пример кода для быстрого перебора строк огромного файла:


// Открываем файл как поток для чтения
ФайловыйПоток = Новый ФайловыйПоток(ПутьКФайлу, РежимОткрытияФайла.Открыть);
// Инициализируем чтение текста из потока
ЧтениеТекста = Новый ЧтениеТекста(ФайловыйПоток); 

СтрокаДанных = ЧтениеТекста.ПрочитатьСтроку();
Пока СтрокаДанных <> Неопределено Цикл 
    // Здесь выполняем обработку строки или фильтрацию
    // Не сохраняем данные в массив или ТЗ без необходимости!
    
    СтрокаДанных = ЧтениеТекста.ПрочитатьСтроку();
КонецЦикла;

ЧтениеТекста.Закрыть();
ФайловыйПоток.Закрыть();

Решение 2: Разделение файла на порции (Chunking)

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

Разберем алгоритм разделения файла на части. Мы будем читать исходный поток и каждые N строк создавать новый файл-результат:


Процедура РазделитьФайл(ИмяИсходногоФайла, КоличествоСтрокВПорции)
    ФайловыйПоток = Новый ФайловыйПоток(ИмяИсходногоФайла, РежимОткрытияФайла.Открыть);
    ЧтениеТекста = Новый ЧтениеТекста(ФайловыйПоток);
    
    НомерФайла = 1;
    СчетчикСтрок = 0;
    
    ТекСтрока = ЧтениеТекста.ПрочитатьСтроку();
    Пока ТекСтрока <> Неопределено Цикл
        
        Если СчетчикСтрок % КоличествоСтрокВПорции = 0 Тогда
            ИмяЧасти = "part_" + Формат(НомерФайла, "ЧГ=0") + ".csv";
            Запись = Новый ЗаписьТекста(ИмяЧасти, КодировкаТекста.UTF8);
            НомерФайла = НомерФайла + 1;
        КонецЕсли;
        
        Запись.ЗаписатьСтроку(ТекСтрока);
        СчетчикСтрок = СчетчикСтрок + 1;
        
        Если СчетчикСтрок % КоличествоСтрокВПорции = 0 Тогда
            Запись.Закрыть();
        КонецЕсли;
        
        ТекСтрока = ЧтениеТекста.ПрочитатьСтроку();
    КонецЦикла;
    
    ЧтениеТекста.Закрыть();
КонецПроцедуры

Решение 3: Оптимизация записи в базу данных

Запись 15 миллионов элементов справочника — это самая ресурсозатратная часть. Даже при скорости записи 100 элементов в секунду процесс займет около 42 часов. Чтобы ускорить запись, проанализируем следующие рекомендации:

  1. Использование транзакций: Оборачивайте запись порций данных (от 500 до 5000 элементов) в одну транзакцию с помощью НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это радикально сокращает количество обращений к диску.
  2. Отключение бизнес-логики: При записи объекта используйте свойство ОбменДанными.Загрузка = Истина. Это позволит миновать проверки в процедурах ПередЗаписью и ПриЗаписи, а также отключит срабатывание подписок на события.
  3. Синхронизация через временные таблицы: Вместо поиска каждого товара методом НайтиПоНаименованию() в цикле (что порождает 15 млн запросов к БД), загружайте порцию в ТаблицуЗначений, передавайте её в запрос и сопоставляйте с существующими данными справочника одним запросом.

Решение 4: Использование внешних инструментов (SQL и СУБД)

Если время загрузки критично (например, прайс нужно обновлять ежедневно), стоит рассмотреть возможность использования инструментов СУБД:

Рекомендации по фильтрации данных

Часто заказчику не нужны все 15 миллионов позиций. Рассмотрим ситуацию, когда из прайса нужно выбрать только товары определенных брендов. Фильтрацию нужно выполнять на этапе потокового чтения. Разбирая строку через СтрРазделить(), мы проверяем условие вхождения бренда и записываем в ТаблицуЗначений для обработки только те строки, которые прошли фильтр. Это позволит сократить объем данных для записи в справочники в десятки или сотни раз.

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

  1. Используем ФайловыйПоток для чтения.
  2. Применяем фильтры «на лету», не сохраняя лишние строки.
  3. Нарезаем отфильтрованные данные на порции по 50-100 тыс. строк.
  4. Каждую порцию обрабатываем в фоновом задании: синхронизируем с БД через запрос к ТЗ и записываем новые объекты в транзакции с флагом ОбменДанными.Загрузка.
  5. Обязательно выводим лог выполнения для контроля скорости и возможных ошибок в данных.
← На главную