ХранилищеЗначения в 1С 8.3: как записать, прочитать и не раздуть базу
Содержание
Хранилище значений в 1С — это тип ХранилищеЗначения, который упаковывает любое сериализуемое значение (файл, картинку, таблицу, структуру) в двоичный вид и кладёт его в одно поле базы данных. Создают его конструктором Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9)), а достают обратно методом Получить(). Других методов у объекта нет: изменить содержимое «на месте» нельзя, только заменить хранилище целиком.
Настройка хранилища значений сводится к трём решениям: где лежит поле (в самом объекте или в отдельном справочнике/регистре), сжимать ли данные и как часто чистить устаревшее. От этих решений зависит, будет ли база работать быстро через год или вырастет до десятков гигабайт и начнёт тормозить при открытии каждого документа.
Как устроено хранилище значений внутри платформы
ХранилищеЗначения — обычный тип реквизита, который выбирают в конфигураторе так же, как Строка или Число. В таблице СУБД ему соответствует одна колонка с двоичными данными. Платформа сериализует значение во внутренний формат, при необходимости сжимает его и записывает этот поток в колонку.
Что можно положить внутрь:
- 📄
ДвоичныеДанные— файлы любого формата: PDF, XLSX, XML, архивы; - 🖼️
Картинка— изображения для печатных форм и карточек; - 📊
ТаблицаЗначений,Массив,Структура,Соответствие— настройки, кэши, результаты расчётов; - 🔗 ссылки на объекты базы и примитивные типы.
Нельзя положить то, что платформа не умеет сериализовать: формы, COM-объекты, объекты справочников и документов (СправочникОбъект, ДокументОбъект), открытые потоки и соединения.
Чем хранилище значений не является
Под похожими названиями в 1С живут три разные вещи. Путаница здесь частая, поэтому разведём их сразу:
| Название | Что это | Где встречается |
|---|---|---|
Хранилище значений (ХранилищеЗначения) | Тип данных для реквизита или ресурса | Справочники, документы, регистры |
| Временное хранилище | Область в памяти сеанса для передачи данных между клиентом и сервером | ПоместитьВоВременноеХранилище() |
| Хранилище настроек | Механизм сохранения пользовательских настроек отчётов и форм | ХранилищеОбщихНастроек и подобные |
| Хранилище конфигурации | Система групповой разработки с захватом объектов | Конфигуратор, меню Конфигурация → Хранилище конфигурации |
Эта статья — только про первый пункт, тип данных.
Почему поле нельзя использовать в запросе как обычное
Платформа не знает, что лежит внутри двоичного потока. Поэтому поле типа ХранилищеЗначения:
- ❌ нельзя проиндексировать;
- ❌ нельзя использовать в условии
ГДЕ, вУПОРЯДОЧИТЬ ПО,СГРУППИРОВАТЬ ПОи в соединениях; - ❌ нельзя сравнивать с другим значением;
- ✅ можно только выбрать в списке полей запроса и затем вызвать
Получить().
Если нужен отбор по содержимому (например, по имени файла или дате создания), эти признаки хранят рядом в обычных реквизитах.
Где хранилище значений применяют в типовых конфигурациях
В прикладных решениях на БСП тип встречается постоянно, просто он скрыт за программными интерфейсами:
- 📎 Присоединённые файлы. Сканы договоров, фото, вложения писем. В режиме хранения «в информационной базе» содержимое лежит в отдельном регистре с ресурсом типа
ХранилищеЗначения, а не в самом документе. - 🔐 Безопасное хранилище. Пароли к сервисам, токены, ключи интеграций. БСП пишет и читает их функциями
ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище()иОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(). Доступ к регистру открыт только привилегированному режиму. - 🧾 Печатные формы и макеты. Пользовательские макеты, загруженные поверх типовых, хранятся в двоичном виде.
- ⚙️ Настройки обменов и регламентных операций. Структуры параметров удобно сохранить одним полем, не заводя десяток реквизитов.
Общий принцип типовых решений: тяжёлые данные отделены от «рабочего» объекта. Документ «Реализация» не таскает за собой скан накладной — скан живёт в своей таблице и подгружается, только когда пользователь его открыл.
Программная запись и чтение данных
Работу с хранилищем значений ведут на сервере. В тонком клиенте и веб-клиенте с этим типом напрямую не работают: значение достают на сервере и передают на клиент через временное хранилище.
Запись файла в реквизит
Типичный сценарий: пользователь выбрал файл на клиенте, файл уже помещён во временное хранилище, на сервер пришёл его адрес.
&НаСервереБезКонтекста
Процедура СохранитьФайлВСправочник(Ссылка, АдресФайла)
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайла);
СпрОбъект = Ссылка.ПолучитьОбъект();
СпрОбъект.Файл = Новый ХранилищеЗначения(ДанныеФайла, Новый СжатиеДанных(9));
СпрОбъект.Записать();
УдалитьИзВременногоХранилища(АдресФайла);
КонецПроцедуры
Число в СжатиеДанных — уровень сжатия от 0 до 9. Без второго параметра конструктора данные записываются несжатыми.
Чтение без загрузки всего объекта
Ссылка.ПолучитьОбъект() читает все реквизиты и табличные части. Если нужен только файл, это лишняя работа. В конфигурациях на БСП берите одно поле:
Хранилище = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Файл");
ДанныеФайла = Хранилище.Получить();
Если ТипЗнч(ДанныеФайла) = Тип("ДвоичныеДанные") Тогда
ДанныеФайла.Записать(ПолучитьИмяВременногоФайла("pdf"));
КонецЕсли;
Проверка ТипЗнч обязательна: в пустом хранилище Получить() вернёт Неопределено, и вызов метода двоичных данных упадёт.
Вывод картинки на управляемую форму
В данные управляемой формы реквизит объекта типа ХранилищеЗначения не попадает. Для поля картинки нужен адрес — строка. Есть два рабочих способа:
- Навигационная ссылка на поле —
ПолучитьНавигационнуюСсылку(Объект.Ссылка, "Фото"). Картинка подгружается платформой по запросу, в форму ничего не копируется. - Временное хранилище — достаёте
КартинкуилиДвоичныеДанныена сервере и помещаете их черезПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор). Адрес кладёте в реквизит формы типаСтрока, к которому привязано поле картинки.
Второй способ нужен, когда картинку ещё не записали в базу — например, пользователь только что выбрал фото в новом элементе.
Привязывайте временное хранилище к УникальныйИдентификатор формы. Тогда данные удалятся при закрытии формы, а не будут висеть в памяти сервера до конца сеанса.
Хранение в отдельном регистре сведений
Для данных, которые нужны редко, удобнее отдельный регистр сведений
: измерение Объект (ссылка на владельца), ресурс Данные типа ХранилищеЗначения. Владелец при этом остаётся лёгким.
НаборЗаписей = РегистрыСведений.ДанныеОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Объект.Установить(Ссылка);
Запись = НаборЗаписей.Добавить();
Запись.Объект = Ссылка;
Запись.Данные = Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9));
НаборЗаписей.Записать();
ДанныеОбъектов здесь — имя регистра, который вы создаёте сами. Метод Записать() набора по умолчанию замещает существующие записи по отбору, поэтому повторная запись не создаёт дублей.
Сжатие и оптимизация доступа к хранилищу
Хранилище значений само по себе не медленное. Тормоза появляются, когда двоичные данные читаются там, где они не нужны.
Когда сжатие помогает, а когда нет
| Что храним | Эффект от СжатиеДанных(9) |
|---|---|
| XML, JSON, текст, CSV | Сильный: объём обычно падает в разы |
ТаблицаЗначений, Структура | Хороший: внутренний формат сериализации избыточен |
| XLSX, DOCX | Слабый: эти форматы уже являются ZIP-архивами |
| JPG, PNG, PDF со сканами, ZIP | Почти никакого, только лишняя нагрузка на процессор |
Сжатие тратит процессор сервера при записи и при каждом чтении. Для уже сжатых форматов ставьте уровень 0 или вызывайте конструктор без второго параметра.
Пять приёмов, которые ускоряют работу
- Не выбирайте поле в запросах без нужды.
ВЫБРАТЬ *из справочника с хранилищем тянет двоичные данные каждой строки. Перечисляйте поля явно. - Не держите тяжёлое поле в основном объекте. Каждое
ПолучитьОбъект(), каждое открытие формы, каждая запись документа читают и пишут все реквизиты. Файл на 5 МБ внутри документа — это 5 МБ при каждом проведении. - Читайте одно поле, а не объект.
ЗначениеРеквизитаОбъектаили запрос с одним полем вместоПолучитьОбъект(). - Храните признаки отдельно. Имя файла, размер, дата, тип — обычными реквизитами. Тогда отбор и сортировка работают по индексам, а хранилище читается только у найденной записи.
- Помните про обмены. В распределённой базе и при обмене через XML хранилище передаётся целиком в кодировке Base64. Это примерно на треть больше исходного размера. Крупные файлы в регистрируемых объектах раздувают пакеты обмена.
Хранилище значений в динамическом списке не показывается и не должно туда попадать. Если на форме списка нужна миниатюра, храните её отдельным небольшим полем, а оригинал — в другой таблице.
Очистка хранилища и обслуживание базы
Хранилище со временем накапливает мусор: старые версии файлов, протоколы обменов, кэши расчётов. Чистить его нужно планово.
Как очистить значение
Методов «очистить» у типа нет. Записывают пустое хранилище:
СпрОбъект = Ссылка.ПолучитьОбъект();
СпрОбъект.Файл = Новый ХранилищеЗначения(Неопределено);
СпрОбъект.Записать();
Для регистра сведений проще удалить запись: записать пустой набор с установленным отбором по владельцу.
Регулярная очистка
Удобная схема для накопительных данных — кэшей, логов, временных выгрузок:
- Рядом с хранилищем держите реквизит
ДатаЗаписи. - Создайте регламентное задание, которое раз в сутки выбирает записи старше заданного срока.
- Задание удаляет записи или заменяет хранилище пустым. Обрабатывайте порциями по несколько сотен записей в транзакции, а не всё разом: длинная транзакция держит блокировки и мешает пользователям.
Отбор идёт по дате, а не по содержимому хранилища — по нему отбирать нельзя.
Почему база не уменьшилась после очистки
После удаления данных файл базы на диске не сжимается сам. Место освобождается внутри и переиспользуется для новых записей.
- 🗂️ Файловая база. Уменьшить файл
1Cv8.1CDможно в конфигураторе черезАдминистрирование → Тестирование и исправлениес флажком сжатия таблиц информационной базы. Режим требует монопольного доступа — все пользователи должны выйти из базы. - 🖥️ Клиент-серверная база
. Свободное место возвращает СУБД. В MS SQL Server это сжатие файла данных, в PostgreSQL —
VACUUM FULLдля конкретной таблицы. Обе операции нагружают сервер и блокируют таблицы, поэтому их делает администратор СУБД в нерабочее время.
Перед тестированием и исправлением и перед сжатием на уровне СУБД сделайте резервную копию базы .
Ограничения платформы и типичные ошибки разработчика
Ошибки в коде
- 🔁 Забыли
Получить(). Код обращается кОбъект.Файл.Записать(...)и падает: в реквизите лежитХранилищеЗначения, а неДвоичныеДанные. - ✏️ Пытаются изменить содержимое. Достали
ТаблицаЗначений, добавили строку — и ждут, что изменится хранилище. Не изменится. Нужно создать новое хранилище из изменённой таблицы и записать объект. - 🖥️ Работа на клиенте. Попытка использовать тип в клиентской процедуре тонкого клиента
вызывает ошибку. Всё, что касается хранилища, — в процедурах
&НаСервереили&НаСервереБезКонтекста. - 🔍 Условие по полю в запросе. Запрос с хранилищем в
ГДЕилиУПОРЯДОЧИТЬ ПОне выполнится. Переносите признаки в обычные поля.
Ограничения, о которых узнают поздно
- 🔗 Ссылки внутри хранилища не контролируются. Если в хранилище лежит массив ссылок, а объект потом удалили через удаление помеченных, платформа не найдёт эту ссылку при поиске. Внутри останется битая ссылка. Храните ссылки, от которых зависит логика, в табличной части.
- 🧠
Получить()грузит всё в память. Файл на 300 МБ целиком окажется в памяти рабочего процесса. Несколько таких операций параллельно — и сервер начинает активно использовать файл подкачки. Очень крупные файлы держите в томах на диске. - 📦 Версия сериализации. Значение, записанное в одной конфигурации, читается в другой, только если там существуют те же типы. Хранилище со ссылкой на справочник, которого нет в базе-приёмнике, вернёт битую ссылку.
- 🧾 Отчёты на СКД. Поле хранилища в системе компоновки данных не группируется и не отбирается. Для отчёта по «содержимому» сначала разберите данные в обычные поля.
Частые вопросы о хранилище значений
Какой максимальный размер у хранилища значений?
Можно ли хранить в хранилище значений объект документа?
Чем хранилище значений отличается от реквизита типа ДвоичныеДанные?
ДвоичныеДанные у объектов метаданных нет. Чтобы сохранить файл в базе, двоичные данные заворачивают в ХранилищеЗначения.Попадает ли хранилище значений в выгрузку .dt?
Как узнать, какие таблицы раздувают базу?
ПолучитьСтруктуруХраненияБазыДанных(). Обычно лидеры — регистры присоединённых файлов и версий объектов.