Как быстро получить реквизит из версии объекта в 1С?

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

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

Проблема использования стандартных методов БСП

Обычно получение данных версии выглядит следующим образом: мы обращаемся к функции ВерсионированиеОбъектов.СведенияОВерсииОбъекта(), получаем двоичные данные и разбираем их через ВерсионированиеОбъектов.РазборПредставленияОбъектаXML(). Проанализируем, почему это медленно. Метод БСП полностью восстанавливает структуру объекта из XML-пакета. Если документ содержит сотни строк в табличных частях, платформа тратит время на чтение всего файла, создание структуры в памяти и заполнение всех полей, даже если нам нужно только одно число.

Выясним причину низкой скорости: основная нагрузка ложится на процессор при десериализации XML и на дисковую подсистему при чтении больших объемов Binary Data из базы данных. Если записей в истории много, отчет может формироваться минуты или даже часы.

Решение №1: Переход на платформенный механизм «История данных»

Одним из наиболее эффективных способов ускорения является переход с механизма БСП на встроенный платформенный механизм ИсторияДанных. В отличие от БСП, который работает на уровне прикладного кода, «История данных» реализована на уровне движка платформы и оптимизирована для быстрой записи и чтения изменений.

Разберем, как использовать метод ВыбратьВерсии для получения данных. Этот метод позволяет накладывать отборы и, что самое важное, выбирать только необходимые колонки (реквизиты).

Посмотрим на пример кода, который позволяет получить данные версии:


// Определим отбор по конкретному документу
Отбор = Новый Структура;
Отбор.Вставить("Данные", СсылкаНаДокумент);

// Укажем, какие именно поля нам нужны, чтобы не читать весь объект
Колонки = "Дата, Номер, СуммаДокумента, АвторИзменений";

// Выбираем версии объекта
Выборка = ИсторияДанных.ВыбратьВерсии(Отбор, Колонки);

Пока Выборка.Следующий() Цикл
    // Платформа сама подставит значения из дельты изменений
    Сумма = Выборка.СуммаДокумента;
    Пользователь = Выборка.Пользователь;
    Сообщить("Версия №" + Выборка.НомерВерсии + ", Сумма: " + Сумма);
КонецЦикла;

Преимущества платформенного механизма:

  1. Высокая скорость записи: Погрешность при записи объекта составляет всего около 1% по сравнению с «голой» записью, в то время как БСП может существенно замедлять процесс.
  2. Хранение дельт: Платформа хранит только изменившиеся данные (разницу), а не полную копию объекта (хотя первый снимок всегда полный).
  3. Оптимизированное чтение: При запросе конкретных полей через параметр Колонки, платформа не выполняет десериализацию всего объекта.

Решение №2: Потоковое чтение XML для БСП

Если проект жестко завязан на использование БСП и переходить на «Историю данных» нельзя, проанализируем ситуацию с парсингом XML. Вместо использования тяжелого метода РазборПредставленияОбъектаXML, мы можем применить потоковое чтение с помощью объекта ЧтениеXML.

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


ДвоичныеДанные = ВерсионированиеОбъектов.СведенияОВерсииОбъекта(Ссылка, НомерВерсии).ВерсияОбъекта;
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные));

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

Такой подход в разы быстрее стандартного разбора, так как мы не строим дерево объектов в памяти и прекращаем чтение сразу после получения данных.

Решение №3: Создание вспомогательного регистра сведений

Если отчет должен строиться мгновенно по огромному массиву данных, рассмотрим архитектурное решение. Как советовали опытные разработчики, можно создать свой плоский регистр сведений, например, ИсторияИзмененияСумм.

Алгоритм работы будет следующим:

  1. Создаем подписку на событие ПриЗаписи для нужных документов.
  2. В обработчике сравниваем текущую сумму документа со значением, которое было в базе до записи.
  3. Если сумма изменилась, записываем в наш регистр запись: Документ | Дата | Пользователь | СтараяСумма | НоваяСумма.

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

Сравнение механизмов хранения версий

Выясним разницу между БСП и платформенной историей на техническом уровне:

Механизм БСП:

Платформенная История данных:

Итоги и рекомендации

Проанализировав все варианты, мы можем сделать следующие выводы:

Если вы только проектируете систему или имеете возможность изменить конфигурацию, использование платформенной «Истории данных» является приоритетным. Это обеспечит максимальную производительность как при записи, так и при последующем чтении реквизитов версии.

Если же вы ограничены существующей инфраструктурой БСП, откажитесь от стандартных методов разбора XML в пользу потокового чтения через ЧтениеXML. Это даст ощутимый прирост скорости без кардинальной переработки системы.

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

← На главную