Почему медленно работает V83.COMConnector на сервере 1С x64 при переносе данных и как это исправить?

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

Перенос больших объемов данных между базами 1С через COM-соединение (V83.COMConnector) — распространенная задача разработчика. Однако при переносе сотен тысяч элементов справочников или документов часто возникает ситуация, когда на локальной файловой базе код отрабатывает за пару часов, а при переносе с 64-битного сервера 1С:Предприятия в файловую базу процесс растягивается на десятки часов или зависает. Разберем по шагам причины резкого падения производительности и рассмотрим эффективные способы оптимизации алгоритма переноса.

Выясним причины низкой скорости работы через COM на сервере

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

  1. Накладные расходы COM-маршалинга (Marshaling): Каждое обращение к свойству или вызов метода объекта через COM-коннектор представляет собой межпроцессный вызов. Если в цикле на 500 000 элементов мы вызываем СоздатьЭлемент(), обращаемся к 5–6 свойствам и вызываем Записать(), суммарно выполняется более 3 500 000 межпроцессных COM-обращений. На 64-разрядном сервере эти задержки взаимодействия рабочего процесса rphost и COM-компонента накапливаются колоссально.
  2. Неявные транзакции на каждый объект: В файловой базе данных (.1CD) каждый отдельный вызов метода Записать() вне явной транзакции открывает, фиксирует и принудительно сбрасывает транзакцию на диск. 500 000 отдельных транзакций создают непрерывные блокировки файла и вызывают перестроение индексов.
  3. Обращение через ссылку в выборке: Использование конструкции Выборка.Ссылка.Реквизит вместо прямого Выборка.Реквизит заставляет платформу выполнять дополнительное неявное чтение объекта из базы данных для каждого реквизита.
  4. Срабатывание событий и проверок: При стандартной записи элементов во внешней базе выполняются обработчики событий (ПередЗаписью, ПриЗаписи), проверки заполнения и механизмы контроля уникальности кодов, что значительно увеличивает время обработки каждого объекта.

Способ 1. Устранение лишних обращений через ссылку в выборке

Посмотрим на частую ошибку в коде выборки. Обращение через Выборка.Ссылка.ИмяПоля инициирует дополнительные запросы к СУБД для получения значения каждого поля, тогда как метод Выбрать() уже загрузил эти значения в саму выборку.

Было (неэффективный код):


Выборка = Справочники.ДокументыНачислений.Выбрать();
Пока Выборка.Следующий() Цикл
    БазаДок = БАза.Справочники.ДокументыНачислений.СоздатьЭлемент();
    БазаДок.Код = Выборка.Ссылка.Код;
    БазаДок.Месяц = Выборка.Ссылка.Месяц;
    БазаДок.Год = Выборка.Ссылка.Год;
    БазаДок.ФИО = Выборка.Ссылка.ФИО;
    БазаДок.Сумма = Выборка.Ссылка.Сумма;
    БазаДок.Записать();
КонецЦикла;

Стало (исправленный базовый код):


Выборка = Справочники.ДокументыНачислений.Выбрать();
Пока Выборка.Следующий() Цикл
    БазаДок = БАза.Справочники.ДокументыНачислений.СоздатьЭлемент();
    БазаДок.Код = Выборка.Код;
    БазаДок.Месяц = Выборка.Месяц;
    БазаДок.Год = Выборка.Год;
    БазаДок.ФИО = Выборка.ФИО;
    БазаДок.Сумма = Выборка.Сумма;
    БазаДок.Записать();
КонецЦикла;

Это исключает избыточные обращения к базе-источнику, однако проблема COM-маршалинга и медленной поштучной записи в файловую базу-приемник все еще остается.

Способ 2. Пакетная фиксация транзакций и отключение проверок

Рассмотрим, как оптимизировать запись на стороне базы-приемника без изменения архитектуры переноса. Для этого обернем запись в явные транзакции порциями по 1 000 – 5 000 элементов и отключим бизнес-логику с помощью режима загрузки данных ОбменДанными.Загрузка = Истина.


ПараметрыПодключения = "File=""" + КаталогБазы + """;Usr="""";Pwd="""";";
Коннектор = Новый COMОбъект("V83.COMConnector");
БАза = Коннектор.Connect(ПараметрыПодключения);

Выборка = Справочники.ДокументыНачислений.Выбрать();
РазмерПорции = 2000;
Счетчик = 0;

БАза.НачатьТранзакцию();

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

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

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

Способ 3. Передача пакетов данных через сериализованные таблицы

Разберем наиболее производительный подход при работе через COM: минимизацию количества межпроцессных вызовов. Вместо миллионов COM-обращений мы формируем в базе-источнике таблицу значений, сериализуем ее в строку и передаем в базу-приемник один раз на 10 000 – 50 000 строк. На стороне приемника экспортная процедура общего модуля восстанавливает таблицу и быстро записывает элементы в одной локальной транзакции.

Код в базе-источнике:


ПараметрыПодключения = "File=""" + КаталогБазы + """;Usr="""";Pwd="""";";
Коннектор = Новый COMОбъект("V83.COMConnector");
БАза = Коннектор.Connect(ПараметрыПодключения);

ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Код");
ТаблицаДанных.Колонки.Добавить("Месяц");
ТаблицаДанных.Колонки.Добавить("Год");
ТаблицаДанных.Колонки.Добавить("ФИО");
ТаблицаДанных.Колонки.Добавить("Сумма");

Выборка = Справочники.ДокументыНачислений.Выбрать();
Счетчик = 0;
РазмерПакета = 10000;

Пока Выборка.Следующий() Цикл
    НоваяСтрока = ТаблицаДанных.Добавить();
    НоваяСтрока.Код = Выборка.Код;
    НоваяСтрока.Месяц = Выборка.Месяц;
    НоваяСтрока.Год = Выборка.Год;
    НоваяСтрока.ФИО = Выборка.ФИО;
    НоваяСтрока.Сумма = Выборка.Сумма;
    
    Счетчик = Счетчик + 1;
    
    Если Счетчик % РазмерПакета = 0 Тогда
        СтрокаДанных = ЗначениеВСтрокуВнутр(ТаблицаДанных);
        БАза.ОбработкаДанныхСервер.ОбработатьСыруюТаблицу(СтрокаДанных);
        ТаблицаДанных.Очистить();
    КонецЕсли;
КонецЦикла;

Если ТаблицаДанных.Количество() > 0 Тогда
    СтрокаДанных = ЗначениеВСтрокуВнутр(ТаблицаДанных);
    БАза.ОбработкаДанныхСервер.ОбработатьСыруюТаблицу(СтрокаДанных);
    ТаблицаДанных.Очистить();
КонецЕсли;

Код процедуры в общем модуле базы-приемника (с флагом Сервер и Вызов сервера):


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

Альтернативные способы переноса больших объемов данных

Если объем данных превышает миллионы записей, рекомендуется отказаться от COM-соединения в пользу более надежных и масштабируемых механизмов платформы:

  1. Выгрузка через файлы XML или JSON: Формирование потокового файла через объекты ЗаписьXML / ЗаписьJSON на стороне источника и чтение через ЧтениеXML / ЧтениеJSON на стороне приемника. Этот метод полностью независим от разрядности сервера, разрядности библиотек COM и не вызывает утечек памяти при многочасовой выгрузке.
  2. Использование HTTP-сервисов: Создание легковесного сервиса в базе-приемнике, принимающего данные пачками по протоколу HTTP/HTTPS в формате JSON.
  3. Локализация файловой базы: Если перенос через COM выполняется разово, файловую базу-приемник необходимо размещать на локальном SSD-диске того же сервера, где работает служба rphost, чтобы полностью исключить сетевые задержки и блокировки файловой системы по протоколу SMB.
← На главную