Почему зависает регламентное задание при загрузке цен из большого файла XML и как это исправить?

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

При автоматизации импорта данных часто возникает ситуация: регламентное задание, которое успешно загружает небольшие тестовые файлы, при работе с реальными объемами (например, прайс-листы на 50 000–100 000 строк) намертво вешает сервер 1С. В консоли администрирования появляются многочисленные блокировки, сеансы пользователей перестают отвечать, а вход в программу блокируется вплоть до перезапуска службы агента сервера. Разберем детально, почему это происходит, и рассмотрим архитектурно правильные способы решения проблемы.

Анализ причин зависания сервера и блокировок

Выясним причину, почему выполнение фонового задания приводит к коллапсу всей информационной базы. Здесь действует комплекс факторов:

  1. Эскалация блокировок СУБД (Lock Escalation): при создании и проведении одного документа с десятками тысяч строк СУБД (например, MS SQL Server) удерживает огромное количество построчных блокировок. При превышении порога (обычно от 5000 блокировок на таблицу) СУБД выполняет эскалацию — блокирует целевую таблицу целиком (регистры цен, служебные таблицы). В результате все параллельные регламентные задания и пользователи встают в очередь ожидания блокировки.
  2. Утечки и фрагментация памяти процесса rphost: чтение тяжелого XML-файла через фабрику XDTO целиком выгружает всю объектную модель документа в оперативную память. Для 75 000 строк это приводит к резкому росту расхода RAM рабочим процессом rphost, сильной фрагментации адресного пространства и падению производительности.
  3. Сверхдлинная транзакция проведения: запись документа установки цен и формирование движений по регистру сведений в одной транзакции занимает длительное время. Пока транзакция открыта, удерживаются блокировки данных, препятствуя выполнению параллельных задач (например, отложенных движений или обменов).

Способ 1. Потоковое чтение XML-файла частями

Откажемся от считывания всего файла XML целиком за один вызов ФабрикаXDTO.ПрочитатьXML(). Перейдем на последовательное потоковое чтение с помощью объекта ЧтениеXML, считывая через XDTO только отдельные узлы строк. Это позволит расходовать минимальный фиксированный объем оперативной памяти вне зависимости от размера исходного файла.

Посмотрим на пример реализации потокового обхода:


ЧтениеXMLФайла = Новый ЧтениеXML;
ЧтениеXMLФайла.ОткрытьФайл(ПутьКФайлу);

ТипЭлементаНоменклатуры = ФабрикаXDTO.Тип("http://v8.1c.ru/edi", "СтрокаПрайса");

Пока ЧтениеXMLФайла.Прочитать() Цикл
    Если ЧтениеXMLФайла.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXMLФайла.Имя = "СтрокаПрайса" Тогда
        ОбъектСтроки = ФабрикаXDTO.ПрочитатьXML(ЧтениеXMLФайла, ТипЭлементаНоменклатуры);
        
        // Обработка данных конкретной строки
        ОбработатьСтрокуПрайса(ОбъектСтроки);
    КонецЕсли;
КонецЦикла;

ЧтениеXMLФайла.Закрыть();

Способ 2. Дробление данных на пакеты документов

Главная ошибка при загрузке больших объемов номенклатуры — попытка записать все 75 000 строк в один документ Установка цен номенклатуры. Оптимальный размер табличной части для документов установки цен в типовых конфигурациях составляет от 1 000 до 3 000 строк.

Разберем по шагам алгоритм пакетной записи:

  1. Организуем счетчик обработанных строк внутри цикла чтения.
  2. Создаем новый документ и заполняем его табличную часть.
  3. При достижении лимита пакета (например, 2 000 строк) записываем документ методом ДокументОбъект.Записать(РежимЗаписиДокумента.Запись) или РежимЗаписиДокумента.Проведение, фиксируем транзакцию и очищаем рабочую коллекцию.
  4. Для следующих строк создаем следующий документ.

РазмерПакета = 2000;
НомерСтроки = 0;
ДокументОбъект = Неопределено;

Пока ЧтениеXMLФайла.Прочитать() Цикл
    Если ЧтениеXMLФайла.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXMLФайла.Имя = "СтрокаПрайса" Тогда
        Если ДокументОбъект = Неопределено Тогда
            ДокументОбъект = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
            ДокументОбъект.Дата = ТекущаяДатаСеанса();
            // Заполнение шапки документа
        КонецЕсли;
        
        СтрокаТЧ = ДокументОбъект.Товары.Добавить();
        // Заполнение реквизитов строки
        
        НомерСтроки = НомерСтроки + 1;
        
        Если НомерСтроки % РазмерПакета = 0 Тогда
            ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
            ДокументОбъект = Неопределено;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Если ДокументОбъект <> Неопределено И ДокументОбъект.Товары.Количество() > 0 Тогда
    ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;

Способ 3. Оптимизация сопоставления номенклатуры в памяти

Если внутри цикла обхода 75 000 строк выполнять поиск номенклатуры запросом или методом Справочники.Номенклатура.НайтиПоРеквизиту(), это порождает 75 000 отдельных обращений к СУБД. Это перегружает пул соединений и провоцирует взаимные блокировки.

Рассмотрим правильный подход: перед началом цикла выгрузим соответствие ключевых реквизитов (артикул, штрихкод, код или идентификатор) и ссылок на справочник Справочники.Номенклатура в оперативную память с помощью объекта Соответствие.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.Артикул КАК Артикул
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |	НЕ Номенклатура.ЭтоГруппа
    |	И Номенклатура.Артикул <> """"";

Выборка = Запрос.Выполнить().Выбрать();
КэшНоменклатуры = Новый Соответствие;

Пока Выборка.Следующий() Цикл
    КэшНоменклатуры.Вставить(Выборка.Артикул, Выборка.Ссылка);
КонецЦикла;

В теле цикла сопоставление выполняется мгновенно через вызов КэшНоменклатуры.Получить(АртикулИзXML) без единого обращения к базе данных.

Способ 4. Настройка параметров кластера 1С и расписания

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

  1. Разделение по времени: проверим расписание других тяжелых заданий (слияние индексов ППД, расчеты движений) и разнесем их запуск по времени, чтобы они не пересекались с импортом цен.
  2. Параметры рабочих процессов: в консоли кластера 1С зададим для рабочих процессов rphost параметры Интервал перезапуска (например, 86 400 секунд) и Допустимый объем памяти. Кластер будет автоматически обновлять рабочие процессы без зависания базы и необходимости ручного перезапуска службы.
← На главную