ХранилищеЗначения в 1С 8.3: как записать, прочитать и не раздуть базу

· 9 мин

Содержание

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

Настройка хранилища значений сводится к трём решениям: где лежит поле (в самом объекте или в отдельном справочнике/регистре), сжимать ли данные и как часто чистить устаревшее. От этих решений зависит, будет ли база работать быстро через год или вырастет до десятков гигабайт и начнёт тормозить при открытии каждого документа.

Как устроено хранилище значений внутри платформы

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

Что можно положить внутрь:

  • 📄 ДвоичныеДанные — файлы любого формата: PDF, XLSX, XML, архивы;
  • 🖼️ Картинка — изображения для печатных форм и карточек;
  • 📊 ТаблицаЗначений, Массив, Структура, Соответствие — настройки, кэши, результаты расчётов;
  • 🔗 ссылки на объекты базы и примитивные типы.

Нельзя положить то, что платформа не умеет сериализовать: формы, COM-объекты, объекты справочников и документов (СправочникОбъект, ДокументОбъект), открытые потоки и соединения.

Чем хранилище значений не является

Под похожими названиями в 1С живут три разные вещи. Путаница здесь частая, поэтому разведём их сразу:

НазваниеЧто этоГде встречается
Хранилище значений (ХранилищеЗначения)Тип данных для реквизита или ресурсаСправочники, документы, регистры
Временное хранилищеОбласть в памяти сеанса для передачи данных между клиентом и серверомПоместитьВоВременноеХранилище()
Хранилище настроекМеханизм сохранения пользовательских настроек отчётов и формХранилищеОбщихНастроек и подобные
Хранилище конфигурацииСистема групповой разработки с захватом объектовКонфигуратор, меню Конфигурация → Хранилище конфигурации

Эта статья — только про первый пункт, тип данных.

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

Платформа не знает, что лежит внутри двоичного потока. Поэтому поле типа ХранилищеЗначения:

  • ❌ нельзя проиндексировать;
  • ❌ нельзя использовать в условии ГДЕ, в УПОРЯДОЧИТЬ ПО, СГРУППИРОВАТЬ ПО и в соединениях;
  • ❌ нельзя сравнивать с другим значением;
  • ✅ можно только выбрать в списке полей запроса и затем вызвать Получить().

Если нужен отбор по содержимому (например, по имени файла или дате создания), эти признаки хранят рядом в обычных реквизитах.

Где хранилище значений применяют в типовых конфигурациях

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

  • 📎 Присоединённые файлы. Сканы договоров, фото, вложения писем. В режиме хранения «в информационной базе» содержимое лежит в отдельном регистре с ресурсом типа ХранилищеЗначения, а не в самом документе.
  • 🔐 Безопасное хранилище. Пароли к сервисам, токены, ключи интеграций. БСП пишет и читает их функциями ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище() и ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(). Доступ к регистру открыт только привилегированному режиму.
  • 🧾 Печатные формы и макеты. Пользовательские макеты, загруженные поверх типовых, хранятся в двоичном виде.
  • ⚙️ Настройки обменов и регламентных операций. Структуры параметров удобно сохранить одним полем, не заводя десяток реквизитов.

Общий принцип типовых решений: тяжёлые данные отделены от «рабочего» объекта. Документ «Реализация» не таскает за собой скан накладной — скан живёт в своей таблице и подгружается, только когда пользователь его открыл.

Программная запись и чтение данных

Работу с хранилищем значений ведут на сервере. В тонком клиенте и веб-клиенте с этим типом напрямую не работают: значение достают на сервере и передают на клиент через временное хранилище.

Запись файла в реквизит

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

&НаСервереБезКонтекста
Процедура СохранитьФайлВСправочник(Ссылка, АдресФайла)

    ДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайла);

    СпрОбъект = Ссылка.ПолучитьОбъект();
    СпрОбъект.Файл = Новый ХранилищеЗначения(ДанныеФайла, Новый СжатиеДанных(9));
    СпрОбъект.Записать();

    УдалитьИзВременногоХранилища(АдресФайла);

КонецПроцедуры

Число в СжатиеДанных — уровень сжатия от 0 до 9. Без второго параметра конструктора данные записываются несжатыми.

Чтение без загрузки всего объекта

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

Хранилище = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Файл");
ДанныеФайла = Хранилище.Получить();

Если ТипЗнч(ДанныеФайла) = Тип("ДвоичныеДанные") Тогда
    ДанныеФайла.Записать(ПолучитьИмяВременногоФайла("pdf"));
КонецЕсли;

Проверка ТипЗнч обязательна: в пустом хранилище Получить() вернёт Неопределено, и вызов метода двоичных данных упадёт.

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

В данные управляемой формы реквизит объекта типа ХранилищеЗначения не попадает. Для поля картинки нужен адрес — строка. Есть два рабочих способа:

  1. Навигационная ссылка на поле — ПолучитьНавигационнуюСсылку(Объект.Ссылка, "Фото"). Картинка подгружается платформой по запросу, в форму ничего не копируется.
  2. Временное хранилище — достаёте Картинку или ДвоичныеДанные на сервере и помещаете их через ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор). Адрес кладёте в реквизит формы типа Строка, к которому привязано поле картинки.

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

Привязывайте временное хранилище к УникальныйИдентификатор формы. Тогда данные удалятся при закрытии формы, а не будут висеть в памяти сервера до конца сеанса.

Хранение в отдельном регистре сведений

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

НаборЗаписей = РегистрыСведений.ДанныеОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(Ссылка);

Запись = НаборЗаписей.Добавить();
Запись.Объект = Ссылка;
Запись.Данные = Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9));

НаборЗаписей.Записать();

ДанныеОбъектов здесь — имя регистра, который вы создаёте сами. Метод Записать() набора по умолчанию замещает существующие записи по отбору, поэтому повторная запись не создаёт дублей.

Где хранить данные в хранилище значений?

Сжатие и оптимизация доступа к хранилищу

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

Когда сжатие помогает, а когда нет

Что хранимЭффект от СжатиеДанных(9)
XML, JSON, текст, CSVСильный: объём обычно падает в разы
ТаблицаЗначений, СтруктураХороший: внутренний формат сериализации избыточен
XLSX, DOCXСлабый: эти форматы уже являются ZIP-архивами
JPG, PNG, PDF со сканами, ZIPПочти никакого, только лишняя нагрузка на процессор

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

Пять приёмов, которые ускоряют работу

  1. Не выбирайте поле в запросах без нужды. ВЫБРАТЬ * из справочника с хранилищем тянет двоичные данные каждой строки. Перечисляйте поля явно.
  2. Не держите тяжёлое поле в основном объекте. Каждое ПолучитьОбъект(), каждое открытие формы, каждая запись документа читают и пишут все реквизиты. Файл на 5 МБ внутри документа — это 5 МБ при каждом проведении.
  3. Читайте одно поле, а не объект. ЗначениеРеквизитаОбъекта или запрос с одним полем вместо ПолучитьОбъект().
  4. Храните признаки отдельно. Имя файла, размер, дата, тип — обычными реквизитами. Тогда отбор и сортировка работают по индексам, а хранилище читается только у найденной записи.
  5. Помните про обмены. В распределённой базе и при обмене через XML хранилище передаётся целиком в кодировке Base64. Это примерно на треть больше исходного размера. Крупные файлы в регистрируемых объектах раздувают пакеты обмена.

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

Очистка хранилища и обслуживание базы

Хранилище со временем накапливает мусор: старые версии файлов, протоколы обменов, кэши расчётов. Чистить его нужно планово.

Как очистить значение

Методов «очистить» у типа нет. Записывают пустое хранилище:

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

Для регистра сведений проще удалить запись: записать пустой набор с установленным отбором по владельцу.

Регулярная очистка

Удобная схема для накопительных данных — кэшей, логов, временных выгрузок:

  1. Рядом с хранилищем держите реквизит ДатаЗаписи.
  2. Создайте регламентное задание, которое раз в сутки выбирает записи старше заданного срока.
  3. Задание удаляет записи или заменяет хранилище пустым. Обрабатывайте порциями по несколько сотен записей в транзакции, а не всё разом: длинная транзакция держит блокировки и мешает пользователям.

Отбор идёт по дате, а не по содержимому хранилища — по нему отбирать нельзя.

Почему база не уменьшилась после очистки

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

  • 🗂️ Файловая база. Уменьшить файл 1Cv8.1CD можно в конфигураторе через Администрирование → Тестирование и исправление с флажком сжатия таблиц информационной базы. Режим требует монопольного доступа — все пользователи должны выйти из базы.
  • 🖥️ Клиент-серверная база . Свободное место возвращает СУБД. В MS SQL Server это сжатие файла данных, в PostgreSQL — VACUUM FULL для конкретной таблицы. Обе операции нагружают сервер и блокируют таблицы, поэтому их делает администратор СУБД в нерабочее время.

Перед тестированием и исправлением и перед сжатием на уровне СУБД сделайте резервную копию базы .

Ограничения платформы и типичные ошибки разработчика

Ошибки в коде

  • 🔁 Забыли Получить(). Код обращается к Объект.Файл.Записать(...) и падает: в реквизите лежит ХранилищеЗначения, а не ДвоичныеДанные.
  • ✏️ Пытаются изменить содержимое. Достали ТаблицаЗначений, добавили строку — и ждут, что изменится хранилище. Не изменится. Нужно создать новое хранилище из изменённой таблицы и записать объект.
  • 🖥️ Работа на клиенте. Попытка использовать тип в клиентской процедуре тонкого клиента вызывает ошибку. Всё, что касается хранилища, — в процедурах &НаСервере или &НаСервереБезКонтекста.
  • 🔍 Условие по полю в запросе. Запрос с хранилищем в ГДЕ или УПОРЯДОЧИТЬ ПО не выполнится. Переносите признаки в обычные поля.

Ограничения, о которых узнают поздно

  • 🔗 Ссылки внутри хранилища не контролируются. Если в хранилище лежит массив ссылок, а объект потом удалили через удаление помеченных, платформа не найдёт эту ссылку при поиске. Внутри останется битая ссылка. Храните ссылки, от которых зависит логика, в табличной части.
  • 🧠 Получить() грузит всё в память. Файл на 300 МБ целиком окажется в памяти рабочего процесса. Несколько таких операций параллельно — и сервер начинает активно использовать файл подкачки. Очень крупные файлы держите в томах на диске.
  • 📦 Версия сериализации. Значение, записанное в одной конфигурации, читается в другой, только если там существуют те же типы. Хранилище со ссылкой на справочник, которого нет в базе-приёмнике, вернёт битую ссылку.
  • 🧾 Отчёты на СКД. Поле хранилища в системе компоновки данных не группируется и не отбирается. Для отчёта по «содержимому» сначала разберите данные в обычные поля.

Частые вопросы о хранилище значений

Какой максимальный размер у хранилища значений?
Практический предел задают память сервера и настройки СУБД, а не сам тип. Файлы от сотен мегабайт лучше хранить в томах на диске, а в базе оставлять только сведения о файле.
Можно ли хранить в хранилище значений объект документа?
Нет. Объекты справочников и документов не сериализуются в хранилище. Кладут ссылку на документ или нужные данные, выгруженные в структуру.
Чем хранилище значений отличается от реквизита типа ДвоичныеДанные?
Реквизита типа ДвоичныеДанные у объектов метаданных нет. Чтобы сохранить файл в базе, двоичные данные заворачивают в ХранилищеЗначения.
Попадает ли хранилище значений в выгрузку .dt?
Да. Выгрузка информационной базы содержит все данные таблиц, включая двоичные поля. Поэтому крупные вложения заметно увеличивают размер файла .dt и время его загрузки.
Как узнать, какие таблицы раздувают базу?
В клиент-серверном варианте посмотрите размеры таблиц средствами СУБД и сопоставьте имена с объектами конфигурации через ПолучитьСтруктуруХраненияБазыДанных(). Обычно лидеры — регистры присоединённых файлов и версий объектов.