Проектирование и оценка
Создано 15.10.2026
Обновлено 15.10.2026
Чем JSONB отличается от JSON, когда выбирать колонки или EAV и как проверять индексы. Примеры PostgreSQL, ограничения, обновления и план испытаний.
JSONB — тип PostgreSQL для хранения JSON в разобранном двоичном представлении. Он удобен для изменяемых свойств, настроек и документов, которые читаются как единое целое. По содержимому можно выполнять запросы и создавать индексы, но это не отменяет проектирование структуры данных.
Обычно полезна гибридная модель: идентификаторы, связи, статусы и основные поля остаются в обычных колонках, а переменное содержимое — в JSONB. Выбор проверяют на реальных фильтрах, сортировках, отчётах и обновлениях. Один GIN-индекс не ускоряет любой запрос, а корректный JSON не гарантирует правильность документа.
Ниже — сравнение JSON и JSONB, учебный пример, выбор индексов и порядок проверки решения. Результат для команды — границы JSON-документа, контракт значений и измеренный набор запросов, по которому можно согласовать хранение.
JSON — формат обмена данными. В PostgreSQL json и jsonb — два разных типа хранения этого формата. Первый сохраняет текстовое представление, второй разбирает документ при записи и сохраняет структуру для последующей обработки. Не следует смешивать JSON как внешний формат и json как конкретный тип БД.
| Критерий | json | jsonb |
|---|---|---|
Представление | Сохраняет входной JSON-текст | Хранит разобранную структуру |
Пробелы и порядок ключей объекта | Сохраняются в исходном представлении | Не сохраняются как свойство исходного текста |
Повторяющиеся ключи объекта | Сохраняются; функции обработки используют последнее значение | Остаётся последнее значение |
Обработка содержимого | Требуется повторный разбор текста | Повторный синтаксический разбор не нужен |
Индексация | Возможны индексы подходящих выражений | Есть поддержка операторов JSONB и GIN |
Типовая причина выбора | Нужно сохранить JSON-текст | Нужно регулярно обрабатывать и искать содержимое |
Для обработки содержимого PostgreSQL обычно рекомендует JSONB. Но из этого не следует, что JSONB всегда меньше, быстрее записывается или выигрывает во всех запросах. Разбор при вводе имеет стоимость, а объём хранения и скорость зависят от документа, индексов и нагрузки. Отличия описаны в документации PostgreSQL 18 по JSON-типам.
Если нужно подтвердить точные байты полученного сообщения, сохраните исходное тело отдельно в подходящем виде, например как bytea, вместе с метаданными получения. Даже сохранение JSON-текста не заменяет фиксацию исходного HTTP-тела, его кодировки и подписи. После нормализации и преобразования в JSONB восстановить прежние пробелы и порядок ключей нельзя.
Тип обеспечивает корректность принимаемого JSON-представления с ограничениями PostgreSQL, но не знает, что у насоса обязательна мощность, что её единица — кВт и что отрицательное значение недопустимо. Он также допускает массив или скаляр, если это отдельно не ограничено. Проверка структуры объекта и прикладная проверка его свойств — разные задачи.
SQL NULL, JSON null и отсутствие ключа следует различать. Оператор ->> может вернуть SQL NULL и для отсутствующего свойства, и для JSON null. Если различие важно, проверяйте наличие ключа оператором ? и JSON-значение через ->. Не сводите три состояния к пустой строке при импорте.
Начните с операций над объектом. Какие поля участвуют в связях, что изменяется независимо, какие ограничения нужно гарантировать в БД и по каким значениям будут регулярные отчёты? Сам факт появления новых атрибутов ещё не делает JSONB единственным выбором.
| Сценарий | Предпочтительный ориентир | Что уточнить |
|---|---|---|
Идентификатор, владелец, статус, дата | Обычные типизированные колонки | Уникальность, обязательность, права и жизненный цикл |
Отношение с другим объектом | Внешний ключ или отдельная таблица связи | Удаление, история, кратность и ссылочная целостность |
Небольшое переменное содержимое карточки | JSONB с согласованной структурой | Типы свойств, версии и частота обновлений |
Управляемый справочник атрибутов и независимые значения | EAV с определениями и типизированными значениями | Применимость, единицы, кратность и основные выборки |
Частые числовые отчёты и сортировки | Колонка или проверенное индексируемое выражение | Преобразования типов, статистика и цена поддержки индекса |
В EAV значение является отдельной записью, связанной с определением атрибута. В JSONB переменные свойства обычно собраны в документе строки. У EAV и JSONB различается организация обновлений, запросов, правил и истории; универсальная замена одного другим не обоснована. Базовые критерии и объяснение модели приведены в статье про динамические атрибуты данных.
JSONB особенно удобен, когда документ имеет понятную границу и часто читается целиком: настройки подключения, параметры обработки, дополнительные характеристики. Если две части постоянно изменяют разные процессы, объединение их в один большой документ способно создать конкуренцию за одну строку. В таком случае стоит пересмотреть границы объектов.
Вынос свойства в колонку также не является поражением гибкой модели. Часто изменяемое содержимое и несколько устойчивых полей для поиска существуют рядом. Если данные дублируются, заранее определите источник истины и способ синхронизации. Иначе ускорение запросов может сопровождаться расхождением значений.
Продолжим условный пример насоса и ёмкости. Инвентарный номер и категория лежат в обычных колонках, технические свойства — в attributes. Номер схемы относится к контракту этих свойств. Числа и названия учебные; код демонстрирует операции, а не результат измерения производительности.
CREATE TABLE equipment (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
inventory_number text NOT NULL UNIQUE,
category text NOT NULL,
schema_version integer NOT NULL,
attributes jsonb NOT NULL DEFAULT '{}'::jsonb,
CHECK (jsonb_typeof(attributes) = 'object')
);
INSERT INTO equipment
(inventory_number, category, schema_version, attributes)
VALUES
('P-17', 'pump', 1,
'{"power_kw": 2.2, "flow_m3h": 4.5}'),
('T-18', 'tank', 1,
'{"volume_l": 300}');NOT NULL запрещает SQL NULL в столбце, а jsonb_typeof(...)= 'object' ограничивает верхний уровень объектом. Эта проверка не делает мощность обязательной и не проверяет её диапазон. У всех строк примера версия равна 1, но разные категории могут использовать разные определения контракта, выбранные приложением по категории и версии.
-- -> возвращает JSONB, ->> возвращает text
SELECT inventory_number,
attributes -> 'power_kw' AS power_json,
attributes ->> 'power_kw' AS power_text
FROM equipment
WHERE category = 'pump';
-- Вхождение с сохранением числового типа
SELECT inventory_number
FROM equipment
WHERE attributes @> '{"power_kw": 2.2}'::jsonb;Не путайте число 2.2 и строку "2.2": в запросе на вхождение это разные JSON-значения. -> сохраняет JSON-представление, ->> извлекает текст. Для числового сравнения нужен числовой тип, а не лексикографическое сравнение строк. Операторы описаны в руководстве PostgreSQL по JSON-функциям.
-- Поиск по вхождению в разные части документа
CREATE INDEX equipment_attributes_gin
ON equipment USING gin (attributes);
-- Равенство / диапазон / сортировка по одному числу
CREATE INDEX equipment_power_idx
ON equipment (((attributes ->> 'power_kw')::numeric));
SELECT inventory_number
FROM equipment
WHERE (attributes ->> 'power_kw')::numeric >= 2
ORDER BY (attributes ->> 'power_kw')::numeric;Индекс по числовому выражению предполагает, что все непустые значения power_kw приводятся к numeric. Если в существующих документах лежит строка «неизвестно», создание индекса или запись может завершиться ошибкой. Очистите данные и обеспечьте контракт до создания такого индекса. Отсутствующее свойство даёт NULL; решение о его допустимости остаётся в правилах модели.
| Запрос | Что рассмотреть | Ограничение |
|---|---|---|
Вхождение через | GIN по документу или нужному поддокументу | Нужно проверять селективность и цену записи |
Наличие ключа через | GIN с классом jsonb_ops | jsonb_path_ops не поддерживает операторы существования ключей |
Числовой диапазон или сортировка | B-tree по числовому выражению либо обычной колонке | Выражение запроса должно подходить индексу; значения должны приводиться к типу |
Чтение объекта по id | Первичный ключ | GIN не нужен только ради получения одной строки |
jsonb_ops и jsonb_path_ops поддерживают разные наборы операторов и имеют разные свойства размера и поиска. Вариант path_ops часто полезен для поддерживаемых запросов на вхождение, но не является универсальной оптимизацией. Проверяйте конкретный запрос через EXPLAIN; базовые правила индексов по выражениям также применимы.
Определите контракт содержимого: обязательные свойства, типы, единицы, неизвестные поля и версию. Для JSON-документов можно применять JSON Schema. Проверка на API полезна, но нужно учесть импорт, фоновые задания, административные операции и прямые записи в БД. Наличие схемы в репозитории само по себе не означает, что все эти пути её используют.
Устойчивые локальные ограничения стоит закреплять в БД, если это оправдано: NOT NULL, CHECK, UNIQUE и внешние ключи для выделенных отношений. Небольшой CHECK может защитить форму документа, однако обычный CHECK не заменяет полноценный валидатор произвольной JSON Schema. Для проверки схемы внутри PostgreSQL потребуется отдельное выбранное решение; встроенный тип JSONB этого не делает.
Не забывайте, что CHECK считается выполненным и при результате SQL NULL. Если выражение проверяет извлечённое свойство, отсутствие ключа может не дать отказа. Обязательность нужно описать и проверить явно. Это особенно важно, когда команда переносит правило из приложения в SQL и ожидает те же результаты для неполных документов.
UPDATE equipment
SET attributes = jsonb_set(
attributes, '{power_kw}', '2.5'::jsonb
)
WHERE inventory_number = 'P-17';jsonb_set меняет значение по указанному пути в результате выражения, но это не обещание физически изменить только несколько байтов в хранилище. Обновление создаёт новую версию строки по правилам PostgreSQL и получает блокировку строки. Большие документы, индексы и частые изменения необходимо проверять вместе.
Обновление на сервере одним SQL-выражением отличается от чтения JSON в приложение и последующей записи всего документа. Во втором случае параллельные изменения могут быть потеряны без блокировки или контроля версии. Выберите подходящий механизм конкурентного изменения и проверьте конфликт двух писателей. Граница атомарного документа должна соответствовать бизнес-операции.
Изменение типа, единиц и обязательности требует проверки старых строк и потребителей. Сохраните идентификатор контракта и прикладную версию, подготовьте миграцию и переходный период. Порядок управления версиями разобран в руководстве по реестру схем. Не подменяйте миграцию автоматическим заполнением default во всех старых документах без решения владельца данных.
Проверка маленького примера подтверждает синтаксис и смысл операторов, но не производительность будущей системы. Важны реальные распределения, размеры, конкуренция, конфигурация PostgreSQL и состояние статистики. Порядок чтения плана приведён в документации EXPLAIN.
->>.На выходе нужны описание границы документа, карта обычных и динамических полей, контракт значений, проверенные SQL-запросы и планы, набор индексов и правила конкурентного обновления. Для каждого важного свойства укажите, почему оно находится в колонке или JSONB и какое наблюдение заставит пересмотреть выбор.
Если существующие данные уже распределены по нескольким системам, добавьте сверку и план переноса из руководства по миграции данных. Архитектурное решение должно сопровождаться проверкой сохранённых значений, а не только новой формой таблицы.
Выберите один объект и выпишите его основные запросы. Если сначала нужно разделить устойчивые поля и переменные свойства, начните с модели динамических атрибутов. Затем согласуйте контракт JSON Schema и проверьте индексы на ожидаемой нагрузке.
Обсудить проект
Если хотите применить этот материал к вашему проекту, напишите нам. Поможем уточнить вводные, риски и следующий шаг: оценку, discovery, разработку, интеграцию или сопровождение.
СвязатьсяПредыдущая
JSON Schema и проверка данныхСледующая
Реестр схем и совместимость версий© 2018–2026, ООО «РоботБулл Технолоджи» ИНН 9710065224
ОКВЭД 62.01
Сведения об ИТ-деятельности