При автоматизации импорта данных часто возникает ситуация: регламентное задание, которое успешно загружает небольшие тестовые файлы, при работе с реальными объемами (например, прайс-листы на 50 000–100 000 строк) намертво вешает сервер 1С. В консоли администрирования появляются многочисленные блокировки, сеансы пользователей перестают отвечать, а вход в программу блокируется вплоть до перезапуска службы агента сервера. Разберем детально, почему это происходит, и рассмотрим архитектурно правильные способы решения проблемы.
Выясним причину, почему выполнение фонового задания приводит к коллапсу всей информационной базы. Здесь действует комплекс факторов:
rphost: чтение тяжелого XML-файла через фабрику XDTO целиком выгружает всю объектную модель документа в оперативную память. Для 75 000 строк это приводит к резкому росту расхода RAM рабочим процессом rphost, сильной фрагментации адресного пространства и падению производительности.Откажемся от считывания всего файла XML целиком за один вызов ФабрикаXDTO.ПрочитатьXML(). Перейдем на последовательное потоковое чтение с помощью объекта ЧтениеXML, считывая через XDTO только отдельные узлы строк. Это позволит расходовать минимальный фиксированный объем оперативной памяти вне зависимости от размера исходного файла.
Посмотрим на пример реализации потокового обхода:
ЧтениеXMLФайла = Новый ЧтениеXML;
ЧтениеXMLФайла.ОткрытьФайл(ПутьКФайлу);
ТипЭлементаНоменклатуры = ФабрикаXDTO.Тип("http://v8.1c.ru/edi", "СтрокаПрайса");
Пока ЧтениеXMLФайла.Прочитать() Цикл
Если ЧтениеXMLФайла.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXMLФайла.Имя = "СтрокаПрайса" Тогда
ОбъектСтроки = ФабрикаXDTO.ПрочитатьXML(ЧтениеXMLФайла, ТипЭлементаНоменклатуры);
// Обработка данных конкретной строки
ОбработатьСтрокуПрайса(ОбъектСтроки);
КонецЕсли;
КонецЦикла;
ЧтениеXMLФайла.Закрыть();
Главная ошибка при загрузке больших объемов номенклатуры — попытка записать все 75 000 строк в один документ Установка цен номенклатуры. Оптимальный размер табличной части для документов установки цен в типовых конфигурациях составляет от 1 000 до 3 000 строк.
Разберем по шагам алгоритм пакетной записи:
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись) или РежимЗаписиДокумента.Проведение, фиксируем транзакцию и очищаем рабочую коллекцию.
РазмерПакета = 2000;
НомерСтроки = 0;
ДокументОбъект = Неопределено;
Пока ЧтениеXMLФайла.Прочитать() Цикл
Если ЧтениеXMLФайла.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXMLФайла.Имя = "СтрокаПрайса" Тогда
Если ДокументОбъект = Неопределено Тогда
ДокументОбъект = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
ДокументОбъект.Дата = ТекущаяДатаСеанса();
// Заполнение шапки документа
КонецЕсли;
СтрокаТЧ = ДокументОбъект.Товары.Добавить();
// Заполнение реквизитов строки
НомерСтроки = НомерСтроки + 1;
Если НомерСтроки % РазмерПакета = 0 Тогда
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
ДокументОбъект = Неопределено;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если ДокументОбъект <> Неопределено И ДокументОбъект.Товары.Количество() > 0 Тогда
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
Если внутри цикла обхода 75 000 строк выполнять поиск номенклатуры запросом или методом Справочники.Номенклатура.НайтиПоРеквизиту(), это порождает 75 000 отдельных обращений к СУБД. Это перегружает пул соединений и провоцирует взаимные блокировки.
Рассмотрим правильный подход: перед началом цикла выгрузим соответствие ключевых реквизитов (артикул, штрихкод, код или идентификатор) и ссылок на справочник Справочники.Номенклатура в оперативную память с помощью объекта Соответствие.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
| И Номенклатура.Артикул <> """"";
Выборка = Запрос.Выполнить().Выбрать();
КэшНоменклатуры = Новый Соответствие;
Пока Выборка.Следующий() Цикл
КэшНоменклатуры.Вставить(Выборка.Артикул, Выборка.Ссылка);
КонецЦикла;
В теле цикла сопоставление выполняется мгновенно через вызов КэшНоменклатуры.Получить(АртикулИзXML) без единого обращения к базе данных.
Для предотвращения взаимных блокировок с другими фоновыми процессами настроим окружение:
rphost параметры Интервал перезапуска (например, 86 400 секунд) и Допустимый объем памяти. Кластер будет автоматически обновлять рабочие процессы без зависания базы и необходимости ручного перезапуска службы.