Конвертация данных хороший инструмент, которая позволяет быстро реализовать несложную перегрузку данных между различными базами 1С. С помощью конвертации данных реализованы перенос остатков из типовых конфигураций 1С 7.7 в типовые на базе 1С8.
Когда в очередной раз столкнулся с необходимостью переноса данных из 1С7.7 в 1С8, то столкнулся с проблемой. При выгрузке большого справочника (около 400 тыс. элементов. Все относительно конечно) семерка вылетает с ошибкой «недостаточно памяти…» Проблема в том, что обработка выгрузки использует объектную модель DOM для работы с XML. В результате все дерево XML до записи в файл хранится в памяти. Также столкнулся с тем, что при выгрузке большого числа элементов, чем дольше работает выгрузка тем медленнее она работает.
Решил немного переделать обработку выгрузки, чтобы исключить эти узкие места.
Первая доработка. Возможность формирования файла xml большого размера.
Доработка заключается в потоковой записи данных XML сразу в файл. На IS видел публикацию в которой автор писал, что использовал другой объект для работы с XML, но подход вроде такой же – запись сразу в файл.
Для потоковой записи в файл использую объект FileSystemObject Windows Script Host. Доработки следующие.
1.Добавить описание переменных
Перем ФСО;
Перем ХМЛФайл;
2.Изменить процедуру Выгрузить()
Процедура Выгрузить()
// Добавлено. Начало
ФСО = СоздатьОбъект("Scripting.FileSystemObject");
ХМЛФайл = ФСО.CreateTextFile(ИмяФайлаДанных, 1, 1);
// Добавлено. Конец
ВыполнитьВыгрузку();
// Добавлено. Начало
ХМЛФайл.Close();
// Добавлено. Конец
Если Форма.МодальныйРежим() = 0 Тогда
//Предупреждение("Выгрузка данных завершена.");
Сообщить("Выгрузка данных завершена.");
Иначе
Сообщить("Выгрузка данных завершена.");
КонецЕсли;
Форма.Параметр = СписокОшибок;
КонецПроцедуры // Выгрузить()
3.Изменить процедуру ВыгрузитьПоПравилу()
Весь текст процедуры не пишу. Изменения ближе к концу процедуры
Функция ВыгрузитьПоПравилу(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО = "", УзелСсылки = "", ТолькоПолучитьУзелСсылки = 0,
НомерПКО = 0)
………………………………………………………………
// Изменено. Начало
// Запись объекта
//ДобавитьПодчиненный(rootNode, Приемник);
ХМЛФайл.WriteLine(Приемник.xml);
// Изменено. Конец
// Обработчик ПослеВыгрузкиВФайлОбмена
Если ПолучитьРеквизитПКО(НомерПКО, "ПослеВыгрузкиВФайл") = 1 Тогда
КодПравила = СокрЛП(ПолучитьРеквизитПКО(НомерПКО, "Код"));
Отказ = Шаблон("[ПКО_ПослеВыгрузкиВФайлОбмена_" + КодПравила + "(Источник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО, Приемник, УзелСсылки)]");
Если Число(Отказ) = 1 Тогда
Возврат УзелСсылки;
КонецЕсли;
КонецЕсли;
Возврат УзелСсылки;
КонецФункции // ВыгрузитьПоПравилу()
4.Изменить процедуру ИнициализацияФайлаОбмена()
Процедура ИнициализацияФайлаОбмена()
УстановитьАтрибут(rootNode, "ВерсияФормата", "2.0");
УстановитьАтрибут(rootNode, "ДатаВыгрузки", ПолучитьДатуV8(ТекущаяДата(), ТекущееВремя()));
УстановитьАтрибут(rootNode, "НачалоПериодаВыгрузки", ПолучитьДатуV8(ДатаНачала));
УстановитьАтрибут(rootNode, "ОкончаниеПериодаВыгрузки", ПолучитьДатуV8(ДатаОкончания));
УстановитьАтрибут(rootNode, "ИмяКонфигурацииИсточника", мКонфигурацияИсточник);
УстановитьАтрибут(rootNode, "ИмяКонфигурацииПриемника", мКонфигурацияПриемник);
УстановитьАтрибут(rootNode, "ИдПравилКонвертации", мИд);
УстановитьАтрибут(rootNode, "Комментарий", "");
// Добавлено. Начало
ХМЛФайл.WriteLine("ВерсияФормата=" + """2.0"""); //NewNode.xm
ХМЛФайл.WriteLine("ДатаВыгрузки" + "=""" + Строка(ПолучитьДатуV8(ТекущаяДата(), ТекущееВремя())) + """");
ХМЛФайл.WriteLine("НачалоПериодаВыгрузки" + "=""" + Строка(ПолучитьДатуV8(ДатаНачала)) + """");
ХМЛФайл.WriteLine("ОкончаниеПериодаВыгрузки" + "=""" + Строка(ПолучитьДатуV8(ДатаОкончания)) + """");
ХМЛФайл.WriteLine("ИмяКонфигурацииИсточника" + "=""" + Строка(мКонфигурацияИсточник) + """");
ХМЛФайл.WriteLine("ИмяКонфигурацииПриемника" + "=""" + Строка(мКонфигурацияПриемник) + """");
ХМЛФайл.WriteLine("ИдПравилКонвертации" + "=""" + Строка(мИд) + """");
ХМЛФайл.WriteLine("Комментарий" + "=""""");
ХМЛФайл.WriteLine(">");
// Добавлено. Конец
………………………………………………………………
………………………………………………………………
// Добавлено. Начало
ХМЛФайл.WriteLine(УзелПравилаОбмена.xml);
// Добавлено. Конец
КонецПроцедуры // ИнициализацияФайлаОбмена()
5.Изменить процедуру ВыполнитьВыгрузку()
Процедура ВыполнитьВыгрузку()
…………………………………………………
rootNode = DOMDocument.createNode(1, "ФайлОбмена", "");
// Добавлено. Начало.
ХМЛФайл.WriteLine("<" + "ФайлОбмена");
// Добавлено. Конец
…………………………………………………..
// Изменено. Начало
//DOMDocument.appendChild(rootNode);
//DOMDocument.save(ИмяФайлаДанных);
ХМЛФайл.WriteLine("</" + "ФайлОбмена>");
// Изменено. Конец
ВывестиСообщение("Выгружено объектов: " + мСчетчикВыгруженныхОбъектов);
ВывестиСообщение("Окончание выгрузки: " + ТекущаяДата() + " " + ТекущееВремя());
КонецПроцедуры // ВыполнитьВыгрузку()
Вторая доработка. Оптимизация скорости выгрузки.
По мере выгрузки справочника делал простой замер скорости. Считал за сколько минут выгружается допустим 1000 объектов. И по этим данным делал примерный расчет общего времени выгрузки. И чем больше объектов выгружалось, тем больше становилось расчетное время выгрузки. 25 тыс – 4,5 часа. 50тыс. – 6 часов. На 100 тыс – уже 10 часов.
Сделал замер. На три однотипные строчки кода тратилось 78% времени.
УзелСсылки = ВыгруженныеОбъекты.Получить(КлючВыгружаемыхДанных);
ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, Нпп);
ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, УзелСсылки);
Изначально ВыгруженныеОбъекты – это список значений.
Заменил Список значений на индексированную таблицу из компоненты 1С++
Что сделать.
1. Изменил процедуру ПриОткрытии ()
Процедура ПриОткрытии()
// Добавлено. Начало
ЗагрузитьВнешнююКомпоненту("1cpp.dll");
// Добавлено. Конец
…………………………………………………………
КонецПроцедуры // ПриОткрытии()
2. Изменить процедуру ЗагрузитьПКО()
Процедура ЗагрузитьПКО(Знач Порядок = "")
мТаблицаПравилКонвертацииОбъектов.НоваяСтрока();
мТаблицаПравилКонвертацииОбъектов.ТекущаяСтрока(мТаблицаПравилКонвертацииОбъектов.КоличествоСтрок());
//мТаблицаПравилКонвертацииОбъектов.Выгруженные = СоздатьОбъект("СписокЗначений");
ИндТаблица = СоздатьОбъект("ИндексированнаяТаблица");
ИндТаблица.НоваяКолонка("КлючВыгружаемыхДанных");
ИндТаблица.НоваяКолонка("Ссылка");
ИндТаблица.ДобавитьИндекс("Ключ","КлючВыгружаемыхДанных");
мТаблицаПравилКонвертацииОбъектов.Выгруженные = ИндТаблица;
…………………………………………………………………………………………………
КонецПроцедуры // ЗагрузитьПКО()
3. Изменить процедуру ВыгрузитьПоПравилу()
Места изменния найти поиском по слову ВыгруженныеОбъекты
Функция ВыгрузитьПоПравилу(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО = "", УзелСсылки = "", ТолькоПолучитьУзелСсылки = 0,
НомерПКО = 0)
……………………………………………………………
Если НеЗапоминатьВыгруженные = 0 Тогда
// Изменено. Начало
//УзелСсылки = ВыгруженныеОбъекты.Получить(КлючВыгружаемыхДанных);
НомерСтроки = ВыгруженныеОбъекты.НайтиСтроку("Ключ", 1);
Если НомерСтроки = 0 Тогда
УзелСсылки = "";
Иначе
УзелСсылки = ВыгруженныеОбъекты.ПолучитьЗначение(НомерСтроки, "Ссылка");
КонецЕсли;
// Изменено. Конец
Если ПустоеЗначение(УзелСсылки) = 0 Тогда
Возврат УзелСсылки;
КонецЕсли;
КонецЕсли;
…………………………………………………………………….
// Это позволит избежать циклических ссылок
Если НеЗапоминатьВыгруженные = 0 Тогда
// Изменено. Начало
//ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, Нпп);
ВыгруженныеОбъекты.НоваяСтрока();
ВыгруженныеОбъекты.КлючВыгружаемыхДанных = КлючВыгружаемыхДанных;
ВыгруженныеОбъекты.Ссылка = Нпп;
// Изменено. Конец
КонецЕсли;
……………………………………………………………
ВыгрузитьСвойства(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, НомерПКО, ПолучитьРеквизитПКО(НомерПКО, "СвойстваПоиска"),
УзелСсылки, , , ИмяПредопределенногоЭлемента, ВыгрузитьТолькоСсылку);
// Изменено. Начало
//ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, УзелСсылки);
ВыгруженныеОбъекты.НоваяСтрока();
ВыгруженныеОбъекты.КлючВыгружаемыхДанных = КлючВыгружаемыхДанных;
ВыгруженныеОбъекты.Ссылка = УзелСсылки;
// Изменено. Конец
……………………………………………………………
КонецФункции // ВыгрузитьПоПравилу()
После этих изменений выгрузка моего справочника выполнилась за 2 с половиной часа.
С чем так и не смог справиться, так это с ошибкой о нехватке памяти, когда для ПКО НЕ стоит свойство «не запоминать выгруженные. Растет таблица закэшированных xml-фрагментов ссылок на выгруженные объекты. Пришлось для ПКО моего большого справочника поставить «не запоминать выгруженные объекты».