При разработке отчетов по истории изменений объектов в 1С программисты часто сталкиваются с проблемой производительности. Стандартный механизм «Версионирование объектов» из библиотеки стандартных подсистем (БСП) сохраняет данные в виде двоичных данных (XML-файлов). Когда нам нужно вытащить всего один реквизит (например, сумму документа) из тысяч версий, полная десериализация каждого объекта превращается в крайне медленный процесс. Рассмотрим подробнее, как можно оптимизировать эту задачу и какие альтернативные механизмы предоставляет платформа 1С.
Обычно получение данных версии выглядит следующим образом: мы обращаемся к функции ВерсионированиеОбъектов.СведенияОВерсииОбъекта(), получаем двоичные данные и разбираем их через ВерсионированиеОбъектов.РазборПредставленияОбъектаXML(). Проанализируем, почему это медленно. Метод БСП полностью восстанавливает структуру объекта из XML-пакета. Если документ содержит сотни строк в табличных частях, платформа тратит время на чтение всего файла, создание структуры в памяти и заполнение всех полей, даже если нам нужно только одно число.
Выясним причину низкой скорости: основная нагрузка ложится на процессор при десериализации XML и на дисковую подсистему при чтении больших объемов Binary Data из базы данных. Если записей в истории много, отчет может формироваться минуты или даже часы.
Одним из наиболее эффективных способов ускорения является переход с механизма БСП на встроенный платформенный механизм ИсторияДанных. В отличие от БСП, который работает на уровне прикладного кода, «История данных» реализована на уровне движка платформы и оптимизирована для быстрой записи и чтения изменений.
Разберем, как использовать метод ВыбратьВерсии для получения данных. Этот метод позволяет накладывать отборы и, что самое важное, выбирать только необходимые колонки (реквизиты).
Посмотрим на пример кода, который позволяет получить данные версии:
// Определим отбор по конкретному документу
Отбор = Новый Структура;
Отбор.Вставить("Данные", СсылкаНаДокумент);
// Укажем, какие именно поля нам нужны, чтобы не читать весь объект
Колонки = "Дата, Номер, СуммаДокумента, АвторИзменений";
// Выбираем версии объекта
Выборка = ИсторияДанных.ВыбратьВерсии(Отбор, Колонки);
Пока Выборка.Следующий() Цикл
// Платформа сама подставит значения из дельты изменений
Сумма = Выборка.СуммаДокумента;
Пользователь = Выборка.Пользователь;
Сообщить("Версия №" + Выборка.НомерВерсии + ", Сумма: " + Сумма);
КонецЦикла;
Преимущества платформенного механизма:
Колонки, платформа не выполняет десериализацию всего объекта.Если проект жестко завязан на использование БСП и переходить на «Историю данных» нельзя, проанализируем ситуацию с парсингом XML. Вместо использования тяжелого метода РазборПредставленияОбъектаXML, мы можем применить потоковое чтение с помощью объекта ЧтениеXML.
Рассмотрим, как это реализовать. Вместо того чтобы превращать весь XML в структуру, мы будем читать файл последовательно и остановимся, как только найдем тег с нужным реквизитом.
ДвоичныеДанные = ВерсионированиеОбъектов.СведенияОВерсииОбъекта(Ссылка, НомерВерсии).ВерсияОбъекта;
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные));
НужнаяСумма = 0;
Пока ЧтениеXML.Прочитать() Цикл
// Ищем узел с именем нашего реквизита
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "СуммаДокумента" Тогда
НужнаяСумма = ЧтениеXML.ПрочитатьСодержимоеКакСтроку();
Прервать; // Мы нашли то, что искали, остальное читать не нужно
КонецЕсли;
КонецЦикла;
Такой подход в разы быстрее стандартного разбора, так как мы не строим дерево объектов в памяти и прекращаем чтение сразу после получения данных.
Если отчет должен строиться мгновенно по огромному массиву данных, рассмотрим архитектурное решение. Как советовали опытные разработчики, можно создать свой плоский регистр сведений, например, ИсторияИзмененияСумм.
Алгоритм работы будет следующим:
ПриЗаписи для нужных документов.В этом случае получение данных для сводного отчета сведется к простому и быстрому запросу к индексированной таблице регистра, без какого-либо обращения к XML или механизмам версионирования.
Выясним разницу между БСП и платформенной историей на техническом уровне:
Механизм БСП:
ВерсииОбъектов.ВерсияОбъекта имеет тип ХранилищеЗначения.Платформенная История данных:
_DataHistoryVersions).ИсторияДанных.УдалитьВерсии(), что позволяет контролировать размер базы.Проанализировав все варианты, мы можем сделать следующие выводы:
Если вы только проектируете систему или имеете возможность изменить конфигурацию, использование платформенной «Истории данных» является приоритетным. Это обеспечит максимальную производительность как при записи, так и при последующем чтении реквизитов версии.
Если же вы ограничены существующей инфраструктурой БСП, откажитесь от стандартных методов разбора XML в пользу потокового чтения через ЧтениеXML. Это даст ощутимый прирост скорости без кардинальной переработки системы.
Для критически важных отчетов, где скорость получения данных является определяющим фактором, лучшим решением остается дублирование ключевых изменяемых реквизитов в специализированный регистр сведений в момент записи объекта.