Оптимальный способ расчета контрольной суммы объекта/записи регистра (CRC32, MD5, SHA1, SHA256)

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

В данной обработке приведен способ расчета контрольной суммы объекта базы данных 1С. При любом изменении данных объекта, контрольная сумма меняется. Вариант расчета контрольной суммы меняем программно. В моем случае CRC32 как раз подходит — это число.

Это может быть полезно:

  • При сравнении документов в различных базах, и не только РИБ
  • В механизме определения, изменен ли объект в журналах изменений.
  • В тех областях, о которых я еще не знаю.

Вот сам код обработки:

&НаСервере
Процедура Рассчитать()
Объект=Ссылка.ПолучитьОбъект();
ПолучитьХешОбъекта(Объект);
КонецПроцедуры

&НаСервере
Процедура ПолучитьХешОбъекта(Объект)
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();

СериализаторXDTO.ЗаписатьXML(ЗаписьXML,Объект);
ДанныеСтрока=ЗаписьXML.Закрыть();

Хеш=Новый ХешированиеДанных(ХешФункция.CRC32);
Хеш.Добавить(ДанныеСтрока);

Сообщить(Хеш.ХешСумма);
КонецПроцедуры

Обновлено от 03.08.2024! Если нужно получить контрольную сумму ссылочного объекта, то код выше это делает. Если нужно получить контрольную сумму записей регистров сведений, например, то лучше использовать либо наборы записей (медленный способ), либо получить данные записей запросом, и каждую запись преобразовать в структуру, и уже ее использовать для расчета контрольной суммы (быстрый способ). 

Вот примерный код:

СтрокаИзмерений="";

Для каждого Измерение Из ОбъектМетаданных.Измерения Цикл
СтрокаИзмерений=СтрокаИзмерений+?(ПустаяСтрока(СтрокаИзмерений),Измерение.Имя,","+Измерение.Имя);
КонецЦикла;

Если ОбъектМетаданных.ПериодичностьРегистраСведений<>Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
СтрокаИзмерений=СтрокаИзмерений+?(ПустаяСтрока(СтрокаИзмерений),"Период",",Период");
КонецЕсли;

Для каждого Ресурс Из ОбъектМетаданных.Ресурсы Цикл
СтрокаИзмерений=СтрокаИзмерений+?(ПустаяСтрока(СтрокаИзмерений),Ресурс.Имя,","+Ресурс.Имя);
КонецЦикла;

Для каждого Реквизит Из ОбъектМетаданных.Реквизиты Цикл
СтрокаИзмерений=СтрокаИзмерений+?(ПустаяСтрока(СтрокаИзмерений),Реквизит.Имя,","+Реквизит.Имя);
КонецЦикла;

СтруктураДанных=Новый Структура(СтрокаИзмерений);

Выборка=РегистрыСведений[Имя].Выбрать();

Пока Выборка.Следующий() Цикл
ЗаполнитьЗначенияСвойств(СтруктураДанных,Выборка);
ХешСумма=ПолучитьХешОбъекта(СтруктураДанных);
КонецЦикла;

Обновлено от 09.08.2024! Как показала практика, применение способа ПолучитьОбъект() для расчета контрольной суммы не всегда оптимально (если много объектов и немного оперативной памяти) и приводит к нехватке памяти т.к. полученные объекты не удаляются из оперативной памяти, а остаются там до окончания процесса. Код вида "Объект=Неопределено"  никогда не помогает!

Был найден обходной путь, через запрос. При этом память не забивается, а скорость даже возросла.

Вот пример кода для документов:

Запрос = Новый Запрос;
Запрос.Текст ="ВЫБРАТЬ * ИЗ "+ОбъектМетаданных.ПолноеИмя();

Результат=Запрос.Выполнить();
Выборка=Результат.Выбрать();

СтрокаРеквизитов="";
Для каждого Колонка Из Результат.Колонки Цикл
Если Колонка.Имя="ВерсияДанных" ИЛИ Колонка.Имя="МоментВремени" ИЛИ Колонка.Имя="Предсталение" Тогда Продолжить; КонецЕсли;

СтрокаРеквизитов=СтрокаРеквизитов+?(ПустаяСтрока(СтрокаРеквизитов),"",",")+Колонка.Имя;
КонецЦикла;

СтруктураОбъекта=Новый Структура(СтрокаРеквизитов);

Пока Выборка.Следующий() Цикл
ЗаполнитьЗначенияСвойств(СтруктураОбъекта,Выборка);
Для каждого ТабЧасть Из ОбъектМетаданных.ТабличныеЧасти Цикл
СтруктураОбъекта.Вставить(ТабЧасть.Имя,Выборка[ТабЧасть.Имя].Выгрузить());
КонецЦикла;
ХешСумма=ПолучитьХешОбъекта(СтруктураОбъекта);
КонецЦикла;

Функция ПолучитьХешОбъекта(Объект)
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();

СериализаторXDTO.ЗаписатьXML(ЗаписьXML,Объект);
ДанныеСтрока=ЗаписьXML.Закрыть();

Хеш=Новый ХешированиеДанных(ХешФункция.CRC32);
Хеш.Добавить(ДанныеСтрока);

Возврат Хеш.ХешСумма;
КонецФункции;

Возможно, так же будут вам полезны мои разработки(ТопчийДЮ):

  • //infostart.ru/public/1160676/ — Система принудительной блокировки пользователей по заданным условиям  (для платформ 8.3.6+, для ЛЮБЫХ баз)
  • //infostart.ru/public/1076549/ — САПП — система анализа на наличие проблем и/или предложений в ЛЮБЫХ базах 1С, работающих на платформе 8.3.4+
  • //infostart.ru/public/936948/ — Удобная, быстрая и функциональная подсистема обмена между узлами РИБ для УТ 11, УТ 10.3, Альфа — Авто с заменой ссылок в узлах, сравнением документов, командным управлением узлами (для платформы 8.3+)
  • //infostart.ru/public/926433/ — Выгрузка в SPOT 2D / ОРИМИ с произвольной структурой полей в файле для УТ 10.3 и не только (платформа 8.3 +)
  • //infostart.ru/public/912717/ — Гибкая система расчета зарплаты по произвольной мотивации для УНФ 1.6 и не только (Расширение + Конфигурация, платформа 8.3+)
  • //infostart.ru/public/893115/ — QR — штрихкодирование документов БЕЗ изменения печатных форм и конфигурации, с произвольными алгоритмами создания QR -кода и обработки для УТ 11 (все), ERP 2, КА 2, УТ 10.3, Розница 2.2, УНФ 1.6, БП 3
  • //infostart.ru/public/149404/ — Выгрузка-загрузка любых данных между похожими или одинаковыми конфигурациями ЛЮБЫХ баз 1С 8.1-8.3  
  • //infostart.ru/public/127859/ — Быстрый поиск дублей с четким/нечетким поиском по любому сочетанию реквизитов/реквизитов таб. частей с отбором и быстрой заменой значений в ЛЮБЫХ базах 
  • //infostart.ru/public/692252/ — Сравнение данных РИБ или обменивающихся баз с одинаковыми конфигурациями по контрольным суммам выбранных реквизитов, работающих на платформе 8.3…и выше 
  • //infostart.ru/public/201119/  — Проведение/снятие с проведения документов/очистки движений не проведенных, перезаписи ссылочных объектов с отборами для баз 1С 8.1-8.3 
  • //infostart.ru/public/621962/ — "Что? Где? Когда?" или журнал изменений с восстановлением состояния реквизитов ссылочных объектов
  • //infostart.ru/public/543417/ — Одиночная/групповая регистрация объектов/реквизитов объектов для обмена через планы обмена c отборами для ЛЮБЫХ баз 1С на 8.2-8.3 
  • //infostart.ru/public/175028/ — Удаление данных с отбором и построением дерева ссылок в базах 1С 8.1-8.3
  • //infostart.ru/public/420278/ — Загрузка иерархии номенклатуры, картинок, штрихкодов, характеристик, доп. реквизитов, цен, поступлений, номен. поставщиков из xls,xlsx,xlsm,ods,ots,csv для УТ 10.3, БП 2.0, УТ 11.1/11.2/11.3, БП 3, КА 2, ERP 2, УНФ 1.6, Розница 2.2 
  • //infostart.ru/public/427579/ — Мастер выгрузки ЛЮБЫХ данных (остатков, цен, картинок, иерархии, доп. реквизитов, характеристик, штрихкодов и т.д.) в CSV / XLS / XLSX / MXL / ODS / PDF для ЛЮБЫХ баз, работающих на платформе 8.3… и выше 
  • //infostart.ru/public/832852/ — Мониторинг цен закупки / цен продаж и наценки для Управления Торговлей 11 (все), ERP 2, КА 2, Розница 2.2

9 Comments

  1. klinval

    Не понял в чём суть алгоритма вычисления контрольной суммы?

    Или в этом и фишка: скачай обработку, посмотри код и увидишь алгоритм?

    Reply
  2. hakerxp

    (1), в обработке пример расчета контрольной суммы.

    Reply
  3. gubanoff

    Неплохо бы в статье рассказать, как и что.

    Reply
  4. davdykin

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

    Reply
  5. ditp

    (3) gubanoff,

    Ой та шо там рассказывать… получаем некое описание объекта и считаем от него хеш.

    Например так:

    Функция ХэшДокумента(СсылкаНаДокум)
    Докум = СсылкаНаДокум.ПолучитьОбъект();
    мтДок = Докум.Метаданные();
    зап  = Новый ЗаписьXML;
    зап.УстановитьСтроку();
    зап.ЗаписатьНачалоЭлемента(«obj»);
    зап.ЗаписатьАтрибут(«Дата» , XMLСтрока(Докум.Дата));
    зап.ЗаписатьАтрибут(«Номер» , XMLСтрока(Докум.Номер));
    зап.ЗаписатьАтрибут(«Пров» , XMLСтрока(Докум.Проведен));
    зап.ЗаписатьАтрибут(«Удал» , XMLСтрока(Докум.ПометкаУдаления));
    Для Каждого рекв Из мтДок.Реквизиты Цикл
    зап.ЗаписатьАтрибут(рекв.Имя, XMLСтрока(Докум[рекв.Имя]));
    КонецЦикла;
    Для Каждого тч Из мтДок.ТабличныеЧасти Цикл
    зап.ЗаписатьНачалоЭлемента(тч.Имя);
    Для Каждого стр Из Докум[тч.Имя] Цикл
    зап.ЗаписатьНачалоЭлемента(«line»);
    Для Каждого рекв из тч.Реквизиты Цикл
    зап.ЗаписатьАтрибут(рекв.Имя, XMLСтрока(стр[рекв.Имя]));
    КонецЦикла;
    зап.ЗаписатьКонецЭлемента();
    КонецЦикла;
    зап.ЗаписатьКонецЭлемента();
    КонецЦикла;
    зап.ЗаписатьКонецЭлемента();
    
    хеш = Новый ХешированиеДанных(ХешФункция.SHA1);
    хеш.Добавить(зап.Закрыть());
    
    Возврат хеш.ХешСумма;
    КонецФункции
    

    Показать

    В примере — документ, но для всего остального аналогично.

    Вместо SHA1 можно использовать CRC, MD5 или SHA256.

    Reply
  6. hakerxp

    (5), сложно у вас написано. Все намного проще. По просьбе трудящихся выкладываю текст.

    Reply
  7. gubanoff

    (6) ну вот, все стало понятно. Как и предполагалось, выгрузка в ХМЛ и получение хеша от файла. Эталонный подход 🙂

    Reply
  8. cleaner_it

    (6) (7) gubanoff, тогда непонятно, откуда взялся выигрыш по скорости. Для начала нужно рассчитать хеш для миллионов записей, а это тоже время.

    Reply
  9. hakerxp

    (8), получение объекта или создание структуры данных записи, намного быстрее, чем перебирать реквизиты, измерения, ресурсы и искать отличия. Ну и проще. Приведу пример. У меня система сравнивает данные независимых регистров сведений в различных базах РИБ. Таблица общая получается более 1 млн. записей. Так вот — затраты на создание данной таблица по времени — около 10 мин. в одной базе и столько же в другой. Время сравнения по моему алгоритму — так же 10-15 мин. (больше времени тратится на регистрацию данных для обмена, чем на сравнение). Железо не серверное. База и СУБД пашут на простом жестком, не SSD.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *