Почему загрузка 5000 строк из Excel в 1С занимает 1-2 часа и как ускорить процесс до нескольких секунд?

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

Многие разработчики сталкиваются с ситуацией, когда, казалось бы, тривиальная задача загрузки небольшого файла Excel (всего 5-10 тысяч строк) превращается в многочасовой процесс. При этом мониторинг ресурсов сервера показывает странную картину: загрузка процессора минимальна (около 5-6%), оперативная память свободна, но 1С «еле шевелится». В этой статье мы подробно разберем, почему так происходит, проанализируем архитектурные ошибки при работе с COM-объектами и рассмотрим три проверенных способа ускорить загрузку в сотни раз.

Диагностика проблемы: почему процессор «отдыхает»?

Первое, что бросается в глаза в описанной ситуации — низкая утилизация ресурсов (6% CPU). Это классический признак того, что проблема не в вычислительной сложности алгоритма, а в ожидании отклика. Платформа 1С является однопоточной в рамках одного сеанса.

Когда мы используем классическое подключение через COMObject("Excel.Application") и читаем данные в цикле поячеечно, происходит следующее:

  1. 1С отправляет команду процессу Excel: «Дай значение ячейки (Row, Col)».
  2. Процесс 1С приостанавливается и ждет.
  3. Excel получает команду, находит значение, упаковывает его и отправляет обратно.
  4. 1С получает ответ и переходит к следующей итерации.

Этот процесс называется маршалингом вызовов между процессами. Даже если это занимает доли миллисекунды, умножив на 5000 строк и, скажем, 10 колонок (50 000 ячеек), мы получаем колоссальные задержки. Именно поэтому процессор не нагружен — он просто ждет завершения межпроцессного взаимодействия.

Чтобы точно убедиться в этом, выполним рекомендацию по включению замера производительности:

  1. В Конфигураторе перейдите в меню Отладка — Замер производительности.
  2. Выполните вашу обработку загрузки.
  3. Остановите замер и отсортируйте таблицу по колонке «Время».

С вероятностью 99% вы увидите, что львиную долю времени занимают строки вида Лист.Cells(Стр, Кол).Value.

Решение 1: Чтение данных массивом (COMSafeArray)

Это самый эффективный способ ускорить существующий код, переписав его минимально. Вместо того чтобы «стучаться» к Excel 50 000 раз за каждой ячейкой, мы можем попросить его отдать всю область данных одним вызовом. В этом случае накладные расходы на вызов COM возникают всего один раз.

Рассмотрим, как это реализовать. Нам понадобится метод Range и свойство Value. В современных версиях платформы 1С свойство Value для диапазона ячеек возвращает COMSafeArray, который можно легко обойти.

Пример оптимизированного кода:


// Простой шаблон для быстрой загрузки данных из Excel
// Создаем подключение к Excel
Эксель = Новый COMОбъект("Excel.Application");
Книга = Эксель.Workbooks.Open(ПутьКФайлу);
Лист = Книга.Worksheets(1);

// Определяем полезную область данных
// SpecialCells(11) находит последнюю ячейку с данными
ПоследняяЯчейка = Лист.Cells.SpecialCells(11);
ВсегоСтрок = ПоследняяЯчейка.Row;
ВсегоКолонок = ПоследняяЯчейка.Column;

// Получаем диапазон всех данных от A1 до правого нижнего угла
Диапазон = Лист.Range(Лист.Cells(1, 1), Лист.Cells(ВсегоСтрок, ВсегоКолонок));

// САМЫЙ ВАЖНЫЙ МОМЕНТ:
// Читаем все данные в память 1С одним вызовом. 
// Теперь ДанныеМассив - это COMSafeArray.
ДанныеМассив = Диапазон.Value;

// Закрываем Excel сразу, он больше не нужен
Книга.Close(0);
Эксель.Quit();

// Обходим массив в памяти 1С.
// Внимание: COMSafeArray часто имеет нижнюю границу индексов 1, но лучше использовать GetLowerBound
Для СчСтр = ДанныеМассив.GetLowerBound(1) По ДанныеМассив.GetUpperBound(1) Цикл
    Для СчКол = ДанныеМассив.GetLowerBound(2) По ДанныеМассив.GetUpperBound(2) Цикл
        // Обращение к элементам массива происходит мгновенно в оперативной памяти
        ЗначениеЯчейки = ДанныеМассив.GetValue(СчСтр, СчКол);
        // ... ваша логика обработки ...
    КонецЦикла;
КонецЦикла;

Этот метод сокращает время чтения файла с 5000 строками с 1-2 часов до нескольких секунд.

Решение 2: Использование Табличного документа (Native)

Если на сервере, где выполняется код (особенно если это Linux), не установлен Microsoft Excel, метод с COM-объектом работать не будет. В этом случае идеальным вариантом является использование встроенного объекта ТабличныйДокумент.

Платформа 1С умеет самостоятельно читать форматы XLS и XLSX. Этот способ часто называют «нативным».

Преимущества:

Пример реализации:


ТабДок = Новый ТабличныйДокумент;

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

ВысотаТаблицы = ТабДок.ВысотаТаблицы;
ШиринаТаблицы = ТабДок.ШиринаТаблицы;

// Обходим ячейки табличного документа
Для Стр = 1 По ВысотаТаблицы Цикл
    // Получаем область строки, чтобы не обращаться к каждой ячейке отдельно, 
    // хотя в ТабДоке обращение к ячейкам быстрое.
    // Но лучше брать значения конкретных ячеек:
    Артикул = ТабДок.Область(Стр, 1).Текст;
    Цена = ТабДок.Область(Стр, 5).Текст; 
    
    // ... логика обработки ...
КонецЦикла;

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

Решение 3: Ускорение записи данных (Транзакции)

Мы оптимизировали чтение, но загрузка все равно может идти медленно, если мы неправильно пишем данные в базу 1С. Запись 5000 элементов справочника или документов по одному — это 5000 обращений к жесткому диску, пересчетов итогов и индексов.

Чтобы ускорить этот процесс, обязательно используйте транзакции и отключение лишних проверок.

Разберем правильный алгоритм записи:


// Начинаем транзакцию перед циклом
НачатьТранзакцию();

Счетчик = 0;

Для Каждого СтрокаДанных Из МассивДанных Цикл
    
    НовЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
    НовЭлемент.Наименование = СтрокаДанных.Наименование;
    
    // Включаем режим загрузки - это отключает проверки заполнения 
    // и регистрацию изменений для обменов, что сильно ускоряет запись
    НовЭлемент.ОбменДанными.Загрузка = Истина; 
    
    НовЭлемент.Записать();
    
    Счетчик = Счетчик + 1;
    
    // Фиксируем транзакцию каждые, например, 500 строк, 
    // чтобы не переполнять журнал транзакций
    Если Счетчик % 500 = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
    КонецЕсли;

КонецЦикла;

// Фиксируем остаток
Если ТранзакцияАктивна() Тогда
    ЗафиксироватьТранзакцию();
КонецЕсли;

Сводная таблица производительности

Давайте сравним методы, обсуждаемые в сообществе, по скорости работы на файле в 5000 строк:

  1. Excel COM (поячеечно): Очень медленно (~1-2 часа). Постоянный вызов внешнего процесса.
  2. Excel OLE: Медленно. Схожие проблемы с COM.
  3. Табличный документ: Быстро (~5-10 секунд). Нативный механизм 1С.
  4. Excel COM (COMSafeArray/Range.Value): Очень быстро (~1-3 секунды на чтение). Чтение всего массива сразу.

Дополнительные рекомендации по настройке сервера

Если вы все же используете COM-объект на стороне сервера (регламентное задание), убедитесь, что сервер настроен корректно:

Применив метод чтения через массив (Range.Value) или перейдя на ТабличныйДокумент, вы гарантированно решите проблему скорости загрузки файлов Excel.

← На главную