Как правильно использовать ХранилищеЗначения и СжатиеДанных в 1С для хранения текстов?

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

При проектировании метаданных в платформе «1С:Предприятие 8» разработчики часто сталкиваются с необходимостью хранения произвольной текстовой или бинарной информации, которая напрямую не участвует в запросах и поиске, но должна быть привязана к конкретному объекту (например, документу или элементу справочника). Традиционным инструментом для этой задачи выступает реквизит с типом ХранилищеЗначения. Однако при работе с ним возникает множество вопросов: нужно ли использовать сжатие, какую степень сжатия выбрать, и не навредит ли это производительности базы данных?

В этой статье мы подробно разберем, как устроен механизм СжатиеДанных, проанализируем физику хранения двоичных данных в СУБД, выясним, почему сжимать маленькие тексты не имеет смысла, и рассмотрим более изящные архитектурные альтернативы, такие как сериализация в JSON и вынос данных в связанные регистры.

Как работает объект СжатиеДанных в 1С

Объект ХранилищеЗначения предназначен для сохранения любых данных, поддерживающих сериализацию (включая картинки, файлы, структуры, таблицы значений и обычный текст). Конструктор этого объекта выглядит следующим образом:

Новый ХранилищеЗначения(Значение, СжатиеДанных)

Параметр СжатиеДанных является необязательным. Если его не передавать, то по умолчанию данные будут помещены в хранилище без какого-либо сжатия (уровень сжатия 0). Если же мы хотим оптимизировать размер, мы можем передать туда объект Новый СжатиеДанных(Х), где параметр Х определяет степень сжатия.

Рассмотрим подробнее шкалу степеней сжатия:

  1. Значение -1 (по умолчанию для конструктора СжатиеДанных). Если вы создаете объект как Новый СжатиеДанных() без параметров, платформа использует значение по умолчанию. Внутренний алгоритм 1С базируется на открытой библиотеке zlib (алгоритм Deflate). Значение -1 соответствует уровню сжатия 6, что является признанным индустриальным стандартом баланса между нагрузкой на центральный процессор (CPU) сервера 1С и коэффициентом уменьшения размера данных.
  2. Значение 0 (без сжатия). Данные упаковываются «как есть» во внутренний бинарный формат 1С без прохождения через алгоритмы архивации.
  3. Значение 1 (минимальное сжатие / максимальная скорость). Алгоритм осуществляет только базовый поиск повторяющихся последовательностей. Процессор практически не нагружается, но размер файла уменьшается незначительно.
  4. Значение 9 (максимальное сжатие / минимальная скорость). Сервер 1С задействует максимальные вычислительные ресурсы для построения наиболее оптимального дерева Хаффмана и поиска совпадений. Это создает повышенную нагрузку на процессор при записи данных. При этом разница в размере файла по сравнению с уровнем 6 (значение -1) на текстовых данных объемом до нескольких мегабайт обычно составляет доли процента.

Проанализируем пример стандартного использования сжатия при записи текста:


// Пример создания хранилища со сжатием по умолчанию
СтепеньСжатия = Новый СжатиеДанных(-1);
ЭлементХранилища = Новый ХранилищеЗначения(МойТекст, СтепеньСжатия);

Проблема избыточности (Overhead) при сжатии небольших данных

Многие разработчики совершают ошибку, пытаясь сжимать небольшие текстовые фрагменты (от нескольких десятков символов до 2-3 килобайт). Выясним причину, почему в таких сценариях архивация дает противоположный результат — итоговый объем сохраненных данных в СУБД становится больше, чем исходный незажатый текст.

Любой алгоритм сжатия семейства Deflate/ZIP для своей работы требует добавления служебной информации в заголовок архива. В этот заголовок помещаются таблицы частот символов (алфавит), маркеры начала и конца блоков, контрольные суммы (CRC32). Кроме того, сама платформа 1С оборачивает упакованный объект в собственный бинарный формат сериализации, добавляя служебные метаданные типа сохраненного значения.

Для наглядности проанализируем ситуацию:

Правило: Использовать сжатие данных (особенно с высокими коэффициентами 8 и 9) эффективно только тогда, когда размер исходного одиночного текстового блока или файла гарантированно превышает 15–20 Килобайт. Для меньших объемов накладные расходы на структуру архива нивелируют всю пользу.

Ограничения работы с Хранилищем Значения на клиенте

Важно помнить о контекстных ограничениях платформы 1С. Объект ХранилищеЗначения спроектирован как серверный тип данных. Попытка вызвать конструктор Новый ХранилищеЗначения() непосредственно в модуле управляемой формы на клиенте (тонком или веб-клиенте) приведет к критической ошибке компиляции «Тип не определен».

Запись и чтение двоичных данных должны выполняться строго в серверном контексте. На клиенте мы можем оперировать только простыми типами (строками, числами), либо передавать двоичные данные через механизм временного хранилища (используя функции ПоместитьВоВременноеХранилище и ПолучитьИзВременногоХранилища).

Влияние на СУБД (MS SQL, PostgreSQL) и физическое хранение

При записи реквизита с типом ХранилищеЗначения в базу данных, СУБД выделяет под него специальное поле неограниченной длины. В MS SQL Server это обычно тип VARBINARY(MAX), а в PostgreSQL — BYTEA.

С точки зрения СУБД, эти данные являются так называемыми LOB (Large Object — большими объектами). Физически они хранятся вне основной страницы данных таблицы (механизм Out-of-row). В основной строке таблицы хранится лишь 24-байтовая ссылка-указатель на цепочку страниц, где фактически лежат бинарные данные.

Это приводит к следующим последствиям для производительности:

  1. Замедление простых выборок. Даже если вы делаете простой запрос к таблице документов и не выбираете реквизит ХранилищеЗначения, СУБД все равно тратит дополнительные ресурсы на обслуживание указателей страниц. Если же вы случайно выберете это поле в тяжелом запросе (например, при соединении таблиц), СУБД будет вынуждена совершать хаотичные дисковые операции чтения (Random Read) для сборки двоичных данных из отдельных страниц LOB-хранилища.
  2. Проблемы с дисковой подсистемой. Базы данных с большим количеством LOB-данных стремительно раздуваются в размерах. Индексы по таким таблицам становятся фрагментированными, а операции резервного копирования (бэкапы) и обслуживание базы (реиндексация, обновление статистики) начинают занимать критически много времени.
  3. Конфликт компрессии. Современные СУБД поддерживают эффективное сжатие данных на уровне страниц (Page Compression). Если вы предварительно сжали данные на сервере 1С с помощью СжатиеДанных(9), СУБД воспримет эти данные как «белый шум» (высокая энтропия заархивированного потока байт) и не сможет сжать их на уровне диска. При этом нагрузка на CPU сервера 1С вырастет, а выигрыша в дисковом пространстве не произойдет.

Альтернатива №1: Использование строки неограниченной длины и формата JSON

Рассмотрим задачу автора темы: необходимо хранить 8 текстовых полей (длиной до 200 символов каждое), которые могут заполняться хаотично, не участвуют в поиске и нужны только при открытии документа или выводе на печать.

Вместо использования сложного бинарного объекта ХранилищеЗначения, гораздо более красивым, легким и производительным решением будет использование реквизита с типом Строка (неограниченной длины), внутри которой данные будут храниться в структурированном текстовом формате JSON.

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

Разберем по шагам, как реализовать этот механизм в коде. Для свертки полей в одну строку мы будем использовать объект Структура и системные средства работы с JSON.

Код для свертки данных и записи в реквизит документа:


// Создаем структуру с нашими хаотичными полями
ДанныеДляСохранения = Новый Структура;
ДанныеДляСохранения.Вставить("Поле1", "Значение текстового поля 1");
ДанныеДляСохранения.Вставить("Поле2", "Второе значение");
ДанныеДляСохранения.Вставить("Поле8", "Восьмое значение");

// Настраиваем параметры записи JSON
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);

// Сериализуем структуру в строку JSON
ЗаписатьJSON(ЗаписьJSON, ДанныеДляСохранения);
СтрокаДляХранения = ЗаписьJSON.Закрыть();

// Записываем полученную строку в реквизит документа (тип: Строка неограниченной длины)
ОбъектДокумента.ДополнительныеТекстовыеДанные = СтрокаДляХранения;

Код для извлечения данных при открытии формы:


// Получаем строку из реквизита
СтрокаJSON = ОбъектДокумента.ДополнительныеТекстовыеДанные;

Если Не ПустаяСтрока(СтрокаJSON) Тогда
    // Инициализируем чтение JSON
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
    
    // Восстанавливаем структуру данных
    ВосстановленныеДанные = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    // Заполняем реквизиты формы для отображения пользователю
    ЗначениеПоля1 = ?(ВосстановленныеДанные.Свойство("Поле1"), ВосстановленныеДанные.Поле1, "");
    ЗначениеПоля2 = ?(ВосстановленныеДанные.Свойство("Поле2"), ВосстановленныеДанные.Поле2, "");
КонецЕсли;

Использование JSON позволяет легко масштабировать систему. Если завтра вам потребуется добавить 9-е или 10-е поле, вам не придется менять структуру таблиц базы данных и запускать длительную реструктуризацию тяжелых таблиц документов. Вы просто добавите новые ключи в структуру JSON. При этом старые документы без проблем откроются — отсутствующие свойства структуры просто вернут значение по умолчанию.

Альтернатива №2: Перенос данных в Регистр Сведений

Если документов создается действительно много (сотни ежедневно), хранение даже JSON-строк в самой таблице документов может привести к постепенному замедлению работы с этим списком. Всякий раз при чтении списка документов СУБД будет вынуждена работать с перегруженной таблицей.

В этом случае отличной практикой проектирования высоконагруженных систем является вынос справочной «тяжелой» информации в подчиненный **Регистр Сведений** (например, с именем ДополнительныеДанныеДокументов).

Проанализируем структуру такого регистра:

При таком подходе таблица самого документа остается легкой и компактной. Все запросы по проведению, анализу остатков и построению списков работают максимально быстро. А тяжелые текстовые данные запрашиваются из регистра сведений только в один конкретный момент времени — когда пользователь физически открывает форму конкретного документа на своем экране.

Резюме и рекомендации

Подводя итог нашему анализу, сформулируем ключевые правила для эффективной работы с текстами в базе данных 1С:

← На главную