Как оптимизировать хранение и поиск кодов маркировки в 1С при больших объемах данных

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

С внедрением обязательной маркировки товаров (Честный Знак, ЕГАИС) многие разработчики столкнулись с проблемой взрывного роста объема баз данных. Код маркировки может содержать до 150 символов и более. Поскольку платформа 1С использует кодировку Unicode (UTF-16), каждый символ в строке занимает 2 байта. В итоге одна марка требует около 300 байт в основной таблице и столько же в индексе. На объемах в 10–15 миллионов записей это приводит к разрастанию таблиц до десятков гигабайт, что замедляет бэкапы, обслуживание индексов и поиск данных.

В данной статье мы разберем несколько эффективных методов сжатия данных и оптимизации поиска, основанных на опыте практикующих разработчиков. Мы рассмотрим преобразование кодов в хеш-суммы, использование типа УникальныйИдентификатор и метод упаковки байтов в строку («китайская кодировка»).

Анализ проблемы избыточности данных

Проанализируем ситуацию: почему стандартное строковое хранение неэффективно? Коды маркировки состоят из ограниченного набора символов (цифры и латиница). В СУБД MS SQL Server для таких данных обычно используются однобайтовые строки (VARCHAR), но 1С транслирует строковые реквизиты в NVARCHAR, принудительно удваивая объем. Если нам необходимо выполнять поиск по марке, мы вынуждены индексировать это поле, что создает огромные индексные таблицы.

Рассмотрим стратегию перехода к хранению хеша. Вместо индексации 150-символьной строки мы можем вычислить MD5-хеш от кода маркировки. MD5 всегда возвращает 16 байт (128 бит). Эти 16 байт идеально укладываются в тип УникальныйИдентификатор (UUID) платформы 1С. Поиск по UUID в 1С работает максимально быстро, а индекс занимает в десятки раз меньше места.

Решение 1: Использование MD5-хеша и типа УникальныйИдентификатор

Выясним, как преобразовать результат хеширования в формат, понятный платформе 1С. Объект ХешированиеДанных возвращает результат в виде объекта ДвоичныеДанные. Нам необходимо корректно перевести эти байты в строковое представление UUID, соблюдая порядок байтов (Big Endian), чтобы поиск был консистентным.

Рассмотрим пример функции, которая вычисляет MD5 от строки и возвращает УникальныйИдентификатор:


Функция ПолучитьUUIDИзХешаМарки(Знач КодМарки) Экспорт
    // Вычисляем MD5
    Хеширование = Новый ХешированиеДанных(ХешФункция.MD5);
    Хеширование.Добавить(КодМарки);
    ДвоичныеДанныеХеша = Хеширование.ХешСумма; // Получаем 16 байт
    
    // Преобразуем в HEX-строку
    HexСтрока = ПолучитьHexСтрокуИзДвоичныхДанных(ДвоичныеДанныеХеша);
    
    // Формируем строковое представление UUID: 8-4-4-4-12 символов
    // Используем СтрШаблон для ускорения формирования строки
    СтрокаUUID = СтрШаблон("%1-%2-%3-%4-%5", 
        Сред(HexСтрока, 1, 8), 
        Сред(HexСтрока, 9, 4), 
        Сред(HexСтрока, 13, 4), 
        Сред(HexСтрока, 17, 4), 
        Сред(HexСтрока, 21, 12));
        
    Возврат Новый УникальныйИдентификатор(СтрокаUUID);
КонецФункции

Преимущества этого метода:

  1. Размер индекса сокращается в 10–15 раз.
  2. Тип УникальныйИдентификатор индексируется средствами СУБД максимально эффективно.
  3. Вероятность коллизии MD5 на объемах до 100 млн записей крайне мала и в задачах складского учета считается допустимым риском (особенно если поиск идет в рамках конкретной номенклатуры).

Решение 2: Упаковка строки («Китайская кодировка»)

Если нам необходимо хранить саму марку в индексируемом поле (например, для поиска по подстроке или если мы не хотим использовать хеши), можно применить метод «упаковки» байтов. Суть метода заключается в том, что мы берем однобайтовую кодировку (например, windows-1251) и интерпретируем два таких символа как один символ UTF-16LE.

Разберем этот процесс на шагах. Мы превращаем исходную строку в буфер двоичных данных, а затем «читаем» этот буфер так, будто это строка в кодировке UTF-16. В результате в базе данных 1С будет храниться строка из иероглифов, которая ровно в два раза короче оригинала по количеству символов, но занимает столько же байт. Однако для SQL Server это будет означать сокращение физического объема поля NVARCHAR в два раза.

Рассмотрим код для упаковки и распаковки строки:


Функция УпаковатьМаркуВКитайскуюСтроку(Знач ИсходнаяСтрока) Экспорт
    // Дополняем до четного количества байт, если нужно
    Если СтрДлина(ИсходнаяСтрока) % 2 <> 0 Тогда
        ИсходнаяСтрока = ИсходнаяСтрока + " ";
    КонецЕсли;
    
    Буфер = ПолучитьБуферДвоичныхДанныхИзСтроки(ИсходнаяСтрока, "windows-1251");
    // Превращаем каждые 2 байта в один символ UTF-16
    Возврат ПолучитьСтрокуИзБуфераДвоичныхДанных(Буфер, "UTF-16LE");
КонецФункции

Функция РаспаковатьМаркуИзКитайскойСтроки(Знач УпакованнаяСтрока) Экспорт
    Буфер = ПолучитьБуферДвоичныхДанныхИзСтроки(УпакованнаяСтрока, "UTF-16LE");
    Возврат СокрЛП(ПолучитьСтрокуИзБуфераДвоичныхДанных(Буфер, "windows-1251"));
КонецФункции

Важное предупреждение: при использовании этого метода следует избегать вывода «упакованных» строк в пользовательский интерфейс. Платформа 1С может выдать ошибку «Недопустимые символы», если случайная комбинация байтов сформирует служебные символы Unicode. Этот метод предназначен только для хранения и поиска на уровне базы данных.

Решение 3: Оптимизация хранения через ХранилищеЗначения

Проанализируем еще один подход. Если поиск требуется только по хешу (Решение 1), то саму длинную марку (150 символов) вообще не нужно хранить в строковом реквизите. Ее можно поместить в реквизит с типом ХранилищеЗначения.

Посмотрим на пример структуры хранения в Регистре Сведений:

При записи мы вычисляем хеш, устанавливаем его в измерение, а саму строку сжимаем и кладем в ресурс. При поиске (например, при сканировании на ТСД) мы сначала вычисляем хеш отсканированной марки, а затем выполняем поиск в регистре по этому идентификатору. Это позволяет держать индекс максимально компактным.

Сравнение производительности и объемов

Проанализируем результаты тестов, проведенных на реальной базе с 15 миллионами марок. Исходные данные: 6 Гб данных в таблице и 8 Гб индексов. После применения комбинированного метода (очистка строкового наименования, перенос данных в сжатый реквизит и использование 16-байтного хеша для поиска):

Рассмотрим вопрос скорости вычисления хеша. На платформе 8.3 объект ХешированиеДанных работает достаточно быстро. Обработка 100 000 марок занимает около 8–9 секунд. Этого более чем достаточно для потоковой обработки документов поступления или реализации. Если же требуется разовая обработка миллионов записей, можно рассмотреть использование внешних компонент на C++, но для повседневных задач стандартных средств платформы 1С вполне хватает.

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

Подведем итоги нашего исследования. Для оптимизации работы с маркировкой в 1С рекомендуем придерживаться следующих правил:

  1. Не индексируйте полные строки кодов маркировки. Это самая частая причина деградации производительности.
  2. Используйте MD5-хеширование. Храните хеш в поле типа УникальныйИдентификатор. Это стандартный и самый быстрый способ поиска в 1С.
  3. Разделяйте данные. Храните уникальную часть марки (GTIN + Серия) в индексируемых полях, а криптохвост (код проверки) — в ХранилищеЗначения или неиндексируемом строковом поле.
  4. Архивируйте старые данные. Коды маркировки проданных товаров, по которым истек срок возврата, целесообразно переносить во внешние базы (например, SQLite) или текстовые файлы-архивы, удаляя их из основной базы 1С.

Соблюдение этих подходов позволит сохранить стабильную работу базы данных даже при обороте в десятки миллионов единиц маркированной продукции в год.

← На главную