Как оптимизировать чтение XML-файла и избежать повторных проверок объектов в 1С

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

При интеграции систем часто возникает ситуация, когда данные передаются в виде «плоских» XML-файлов, где вся информация сосредоточена в атрибутах узлов. Если файл содержит десятки тысяч элементов, и многие из них ссылаются на одни и то же объекты (например, справочники «Организации», «Контрагенты» или «Номенклатура»), стандартный алгоритм «прочитать — найти в базе — обновить» начинает работать крайне медленно. Основная нагрузка ложится на избыточные обращения к базе данных для поиска и сравнения реквизитов объектов, которые уже были обработаны в рамках текущей сессии загрузки.

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

Использование объекта Соответствие для кэширования объектов

Самый эффективный способ избежать повторных тяжелых проверок — это использование коллекции Соответствие (Map). Идея заключается в том, чтобы при первом появлении объекта в файле выполнить полную проверку (поиск в базе, сравнение реквизитов, запись), а затем сохранить полученную ссылку в памяти. При повторном появлении того же УИД (GUID) в файле, мы просто извлекаем готовую ссылку из памяти без обращения к СУБД.

Рассмотрим реализацию этого метода по шагам. Сначала создадим коллекцию для хранения кэша, где ключом будет строковое представление УИД, а значением — ссылка на объект справочника.


// Создаем кэш в начале процедуры загрузки
КэшОрганизаций = Новый Соответствие;

Пока ЧтениеXML.Прочитать() Цикл
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Элемент" Тогда
        
        УИД_Стр = ЧтениеXML.ЗначениеАтрибута("Организация");
        
        // Проверяем, обрабатывали ли мы эту организацию ранее в этом файле
        СсылкаОрганизация = КэшОрганизаций.Получить(УИД_Стр);
        
        Если СсылкаОрганизация = Неопределено Тогда
            // Объект встретился впервые, выполняем поиск и проверку реквизитов
            УникальныйИд = Новый УникальныйИдентификатор(УИД_Стр);
            СсылкаОрганизация = Справочники.Организации.ПолучитьСсылку(УникальныйИд);
            
            // Проверяем существование объекта в БД
            ОбъектОрганизация = СсылкаОрганизация.ПолучитьОбъект();
            
            Если ОбъектОрганизация = Неопределено Тогда
                // Создаем новый, если не найден
                ОбъектОрганизация = Справочники.Организации.СоздатьЭлемент();
                ОбъектОрганизация.УстановитьСсылкуНового(СсылкаОрганизация);
                ЗаполнитьРеквизитыИзXML(ОбъектОрганизация, ЧтениеXML);
                ОбъектОрганизация.Записать();
            Иначе
                // Если найден, сравниваем реквизиты (логика корректировки)
                Если НужноОбновитьДанные(ОбъектОрганизация, ЧтениеXML) Тогда
                    ЗаполнитьРеквизитыИзXML(ОбъектОрганизация, ЧтениеXML);
                    ОбъектОрганизация.Записать();
                КонецЕсли;
            КонецЕсли;
            
            // Помещаем ссылку в кэш
            КэшОрганизаций.Вставить(УИД_Стр, СсылкаОрганизация);
        КонецЕсли;
        
        // Теперь СсылкаОрганизация гарантированно содержит нужную ссылку
        // Используем её для заполнения основного документа
        НовыйДокумент.Организация = СсылкаОрганизация;
        
    КонецЕсли;
КонецЦикла;

Важный нюанс: Метод ПолучитьСсылку() не делает запрос к базе данных. Он лишь формирует ссылку «в воздухе». Реальное обращение к базе происходит только при вызове ПолучитьОбъект() или при попытке прочитать реквизит через точку (например, Ссылка.Наименование). Использование Соответствие позволяет свести количество этих «тяжелых» вызовов к минимуму — ровно по одному разу на каждый уникальный УИД в файле.

Оптимизация чтения атрибутов через ЧтениеXML

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

Вместо этого мы используем последовательное чтение через объект ЧтениеXML. Проанализируем, как правильно считывать атрибуты, чтобы это было максимально производительно. Использование метода ЗначениеАтрибута() по имени — наиболее читаемый способ, но если атрибутов очень много (30 и более), можно использовать перебор по индексу.


Пока ЧтениеXML.Прочитать() Цикл
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        // Проверяем наличие атрибутов у текущего узла
        Если ЧтениеXML.КоличествоАтрибутов() > 0 Тогда
            // Можно перебрать все атрибуты в цикле, если их состав заранее неизвестен
            Для НомерАтрибута = 0 По ЧтениеXML.КоличествоАтрибутов() - 1 Цикл
                ИмяАтрибута = ЧтениеXML.ИмяАтрибута(НомерАтрибута);
                Значение = ЧтениеXML.ЗначениеАтрибута(НомерАтрибута);
                // Логика обработки
            КонецЦикла;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

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

Пакетная обработка и временные таблицы

Если стоит задача максимально ускорить процесс, можно рассмотреть вариант предварительного сбора всех уникальных идентификаторов из XML. Это позволит выполнить поиск объектов одним запросом к базе данных, что гораздо быстрее, чем 10 000 одиночных поисков.

Проанализируем этот алгоритм:

  1. Проходим по XML-файлу «быстрым» циклом, только считывая уникальные УИД организаций во временную таблицу (или массив).
  2. Передаем этот список в запрос. В запросе соединяем список УИД с таблицей справочника.
  3. Получаем результат запроса, в котором сразу видно: какие объекты уже есть, а какие нужно создать, и каковы их текущие значения реквизитов.
  4. Выгружаем результат запроса в Соответствие или ТаблицуЗначений с индексами для быстрого доступа.
  5. Повторно проходим по XML (или используем данные из первой итерации, если они были сохранены в таблицу) и выполняем запись только тех объектов, которые действительно изменились.

Этот метод сложнее в реализации, но на объемах свыше 50 000 элементов он показывает кратный прирост производительности по сравнению с последовательной обработкой.

Хеширование данных для мгновенного сравнения

Когда у объекта 30 и более атрибутов, код сравнения каждого реквизита (Если Объект.Реквизит1 <> ДанныеXML1 ИЛИ Объект.Реквизит2 <> ДанныеXML2...) становится громоздким и трудноподдерживаемым. Чтобы оптимизировать этот процесс, можно использовать расчет хеш-суммы.

Разберем, как это работает: при записи объекта в 1С мы можем формировать строку из всех его значимых реквизитов и вычислять её хеш (например, MD5). Этот хеш сохраняется в специальный дополнительный реквизит справочника (например, ХешДанных). При следующей загрузке мы вычисляем хеш для строки атрибутов из XML. Если полученный хеш совпадает с тем, что хранится в базе, мы можем со 100% уверенностью утверждать, что ни один из 30 реквизитов не изменился, и пропустить этап перезаписи объекта.


Хеширование = Новый ХешированиеДанных(АлгоритмХеширования.MD5);
СтрокаДляХеша = "";
Для Каждого Атрибут Из СписокАтрибутов Цикл
    СтрокаДляХеша = СтрокаДляХеша + ЧтениеXML.ЗначениеАтрибута(Атрибут);
КонецЦикла;
Хеширование.Добавить(СтрокаДляХеша);
ТекущийХешXML = НРег(СтрЗаменить(Строка(Хеширование.ХешСумма), " ", ""));

Если ОбъектВБазе.ХешДанных <> ТекущийХешXML Тогда
    // Обновляем данные
    ЗаполнитьОбъект(ОбъектВБазе, ЧтениеXML);
    ОбъектВБазе.ХешДанных = ТекущийХешXML;
    ОбъектВБазе.Записать();
КонецЕсли;

Заключение

Оптимизация загрузки XML в 1С строится на трех «китах»:

  1. Минимизация обращений к СУБД через кэширование в Соответствие.
  2. Потоковое чтение данных через ЧтениеXML для экономии оперативной памяти.
  3. Быстрое сравнение состояний через хеширование или предварительную выборку данных запросом.

Внедрение даже одного механизма кэширования ссылок по УИД позволяет сократить время загрузки файла из 10 000 элементов с нескольких минут до нескольких секунд, особенно если в файле присутствует высокая степень дублирования объектов.

← На главную