Сайт использует сookies для хранения данных. Продолжая использовать сайт, вы даёте согласие на работу с этими файлами.

ОК
🧱
Данные
Опубликовано:
14.08.2026
Обновлено:
14.08.2026

JSON vs JSONB vs обычные колонки в PostgreSQL: как выбрать модель данных

Артём Целин

Хранение данных в PostgreSQL — это всегда поиск баланса между строгостью, производительностью и гибкостью. Перед разработчиком часто встаёт вопрос: завести классические реляционные колонки со строгой схемой или положить часть информации в JSON? А если JSON, то какой из двух типов выбрать — json или jsonb? Решение влияет на скорость запросов, удобство миграций и то, насколько легко поддерживать систему в будущем. Разберёмся, как устроены оба типа JSON в PostgreSQL, чем они отличаются от нормализованных таблиц и в каких случаях каждый из подходов оправдан.

Типы JSON и JSONB в PostgreSQL

PostgreSQL поддерживает два типа для работы с JSON-данными: json и jsonb. Оба принимают корректные JSON-документы и гарантируют, что записанная информация соответствует синтаксису JSON. Однако внутри сервер работает с ними принципиально по-разному.

Как PostgreSQL хранит json и jsonb

Тип json хранит исходную текстовую строку — точную копию того, что было передано в запросе. Пробелы, переводы строк, порядок ключей и даже дублирующиеся ключи в объектах остаются нетронутыми. При сохранении происходит только валидация: если строка не является правильным JSON, запись будет отклонена.

Тип jsonb преобразует входную строку в бинарное древовидное представление. Этот процесс убирает незначащие пробелы, сортирует ключи, избавляется от дубликатов (оставляя только последнее значение) и строит структуру, оптимизированную для последующей обработки. После конвертации исходный текст в том виде, в котором он пришёл, восстановлению не подлежит — выходные данные всегда будут нормализованы.

Валидация при вставке и парсинг

Оба типа при вставке проверяют, что входное значение — синтаксически верный JSON. Дальнейшее поведение различается.

json после проверки просто сохраняет текст. Когда впоследствии вы захотите прочитать значение конкретного ключа или отфильтровать строки по содержимому JSON, серверу придётся каждый раз заново разбирать текстовое представление. Это накладные расходы на повторный парсинг при каждой операции.

jsonb проводит парсинг однократно — в момент вставки или обновления. После этого все внутренние операции с JSON-полями идут уже с готовым деревом. Именно поэтому jsonb значительно быстрее в сценариях, где требуется обрабатывать содержимое JSON.

Порядок ключей, пробелы и дубликаты — важные различия для совместимости

Если вашему приложению критично побитовое соответствие исходного JSON тому, что вернёт база, выбор очевиден: только json. Тип json не изменит ни одного пробела и сохранит исходный порядок ключей. Это может быть важно для систем, которые подписывают JSON-документы или проверяют их контрольные суммы.

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

Сравнение JSON и JSONB: когда какой использовать

Поведенческих различий достаточно, чтобы выбор стал неочевидным. Стоит рассмотреть производительность, индексацию и семантику операторов.

Производительность записи и чтения

При вставке json немного быстрее — ведь он не строит внутреннее дерево, а только проверяет синтаксис и записывает текст. Разница на единичных документах обычно незаметна без специальных тестов, но в высоконагруженных потоках записи она может проявиться.

Когда дело доходит до чтения и обработки, картина меняется кардинально. jsonb работает существенно быстрее, потому что парсинг уже выполнен. Если ваш код часто обращается к полям внутри JSON, фильтрует по ним или строит агрегации, jsonb будет значительно эффективнее.

Индексация: GIN для jsonb, отсутствие индексов по содержимому у json

Это, пожалуй, самый весомый аргумент для практического использования. Для jsonb вы можете создать GIN‑индекс, позволяющий быстро искать документы, содержащие определённые ключи или значения, а также проверять вложенные структуры. Существует специализированный класс операторов jsonb_path_ops, оптимизированный для операций типа «содержит ли документ такой‑то фрагмент». С такими индексами запросы с оператором @> превращаются в быстрые просмотры индекса, а не в полное сканирование таблицы.

Для типа json индексация по содержимому недоступна. Можно построить индекс только на всё поле как на текст (как для колонки типа text), но он не поможет искать по отдельным ключам или значениям внутри JSON-документа.

Сохранение точной текстовой копии — зачем может понадобиться тип json

Тип json незаменим, когда необходима неизменность оригинальной строки. Это случается при логировании входящих запросов, аудите обменов с внешними системами, хранении подписанных JSON-документов или в любых случаях, где важно воспроизвести ровно то, что было получено. Если же семантика «точный оригинал» не нужна, выгоднее сразу взять jsonb, чтобы не платить многократным парсингом на чтении.

Семантика операторов и функций для каждого типа

PostgreSQL предоставляет богатый набор операторов как для json, так и для jsonb — стрелочные операторы -> и ->> для доступа к полям, проверки наличия ключей и другие. Однако некоторые продвинутые операторы, такие как контейнмент @> или проверка существования ключа ?, доступны только для jsonb. Практически все функции, начинающиеся с jsonb_*, рассчитаны именно на бинарный тип и не имеют аналогов для json. Если вы планируете активно работать с JSON внутри базы данных, jsonb предложит более мощный инструментарий.

Обычные реляционные колонки — проверенная строгость

Классический реляционный подход с выделенными колонками под каждый атрибут остаётся надёжным фундаментом для большинства приложений.

Сильные стороны нормализованной схемы

Явная типизация каждой колонки обеспечивает целостность данных: СУБД не даст сохранить число в текстовое поле или нарушить ограничение NOT NULL. Связи через внешние ключи защищают от висячих записей. Схема документирует сама себя, и любому разработчику достаточно взглянуть на DDL, чтобы понять структуру. Индексы строятся точно под структуру запросов, оптимизатор получает детальную статистику по каждой колонке. Всё это даёт предсказуемую производительность и упрощает поддержку.

Производительность JOIN и предикатов по сравнению с разбором JSON

При поиске по строго типизированным колонкам PostgreSQL использует узкоспециализированные методы доступа, бинарные сравнения и эффективные индексы B‑tree. Фильтрация по значению обычной колонки на порядки быстрее, чем разбор JSON-документа и извлечение значения даже с GIN‑индексом, особенно когда условия затрагивают несколько полей. Соединения таблиц с индексами по внешним ключам также значительно обгоняют джойны, требующие предварительного извлечения идентификатора из JSONB-поля.

Ограничения гибкости — когда жёсткая схема становится обузой

Жёсткая схема начинает мешать, если структура сущностей часто меняется или заранее неизвестна. Например, каталог товаров с динамическими атрибутами: для каждой категории свой набор характеристик, и добавление нового атрибута в классической модели требует изменения схемы — создания новой колонки или таблицы «сущность-атрибут-значение» (EAV), что усложняет запросы. Логи агрегаторов, принимающих разнородные события от множества источников, тоже не лягут в одну жёсткую таблицу. В таких случаях JSONB становится прагматичным компромиссом.

Гибридные модели: разумное сочетание JSONB и реляционных колонок

На практике вовсе не обязательно выбирать что‑то одно. Часто оптимальным решением оказывается смешанный подход.

Примеры: основные атрибуты в колонках, расширенные/опциональные — в JSONB

Представьте таблицу товаров. Обязательные поля — идентификатор, название, цена, остаток — выносятся в отдельные колонки с правильными типами и ограничениями. А все варьируемые характеристики (материал, цвет, габариты, совместимость) можно упаковать в одну колонку attributes типа jsonb. Это сохраняет быстрые сортировки и фильтрации по основным полям и одновременно даёт гибкость для описания любой категории товаров без постоянных миграций схемы.

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

Индексация отдельных полей JSONB при смешанном подходе

GIN‑индекс на всю колонку jsonb — это хорошо, но иногда нужна более точечная оптимизация. PostgreSQL позволяет создать индекс на конкретное поле внутри JSONB, например:

CREATE INDEX idx_user_preferences_theme ON profiles ((custom_fields->>'theme'));

Также можно построить GIN‑индекс только на нужный набор ключей, а не на весь документ. Это экономит место и даёт быстрый доступ к часто фильтруемым атрибутам, сохраняя общую гибкость гибридной схемы.

Критерии выбора модели данных — практический чек-лист

Перед тем как принять решение, полезно ответить себе на несколько вопросов.

Вопросы, которые нужно задать себе перед выбором

  1. Насколько часто меняется схема? Если атрибуты добавляются каждую неделю и вы устали от ALTER TABLE — JSONB напрашивается.
  2. Какие запросы будут основными? Если вам нужны сложные JOIN, сортировки по многим полям или агрегации с группировкой по типизированным значениям — обычные колонки дадут предсказуемую производительность.
  3. Требуется ли ссылочная целостность? JSONB не умеет внешних ключей, поэтому любые связи с другими таблицами лучше держать в отдельных колонках.
  4. Насколько глубоко вложены данные? Если вложенность больше одного-двух уровней, реляционная модель обычно становится слишком сложной, и JSONB выглядит естественнее.
  5. Важна ли точная текстовая копия? Если нужно сохранить исходный JSON без изменений — только json.
  6. Критична ли производительность записи? В сценариях с очень интенсивным потоком вставки и редким чтением json может дать небольшой выигрыш.

Какому сценарию что подходит

Сценарий Рекомендуемый подход
Строгий финансовый учёт, транзакции с явной схемой Нормализованные колонки
Логирование входящих запросов, аудит API-вызовов json (сохранение точного оригинала)
Каталог товаров с меняющимися атрибутами Гибрид: основные поля колонками, расширенные — jsonb
Хранение настроек пользователя, конфигураций jsonb с точечными индексами
Агрегация и аналитика по содержимому JSON Только jsonb с GIN‑индексами
Типовые CRUD-приложения с чёткими сущностями Классические колонки

Часто задаваемые вопросы

Чем отличается jsonb от json? Главное отличие: json хранит исходный текст без изменений, jsonb переводит его в бинарное дерево. Из-за этого jsonb быстрее на чтение, поддерживает индексы по содержимому и не сохраняет порядок ключей, пробелы и дубликаты.

Когда обычные колонки лучше, чем jsonb? Когда схема стабильна, требуется высокая производительность сложных запросов, важна типизация и ссылочная целостность. Классические колонки выигрывают в сценариях с активным использованием WHERE, JOIN и агрегаций по множеству полей.

Можно ли создать индекс на конкретное поле внутри json? На тип json создать индекс по значению ключей нельзя, только текстовый индекс по всей колонке. Для jsonb можно создать GIN‑индекс на всю колонку или B‑tree/GIN‑индекс на выражение, обращающееся к определённому полю.

Влияет ли jsonb на размер таблицы и скорость TOAST? Да. Большие значения jsonb, как и текстовые, могут вытесняться во внешнее TOAST-хранилище и сжиматься. Это влияет на скорость доступа: чтение TOAST-атрибутов требует дополнительных обращений к диску. Плюс бинарное представление в некоторых случаях занимает больше места, чем сжатый текст того же документа.

Сохраняет ли jsonb порядок ключей? Нет. При конвертации в бинарный формат ключи объекта сортируются, и исходный порядок теряется. Если порядок важен, используйте json либо выносите упорядоченные данные в массив.

Безопасно ли использовать jsonb для данных, получаемых из API? Вполне, если не требуется побитовая идентичность ответа. Однако помните, что дублирующиеся ключи будут потеряны, а формат вывода нормализован. Если клиентское приложение подписывает JSON или сравнивает строки, такое изменение может стать проблемой. В таких случаях логируйте или храните сырой ответ в колонке типа json, а для рабочей обработки используйте jsonb на основе того же документа.

Заключение

Выбор между json, jsonb и реляционными колонками — это не вопрос «что лучше», а вопрос «что уместно сейчас». Тип json — инструмент для точного сохранения входящих документов. Тип jsonb — оптимизированный под обработку формат для полуструктурированных данных, в которых часто приходится копаться. Нормализованные колонки остаются непревзойдёнными по производительности и целостности там, где структура известна и стабильна. В реальных проектах чаще всего побеждает гибридный подход: критичные для запросов поля выносятся в отдельные колонки, а всё, что тяготеет к гибкости, уходит в jsonb. Итоговое решение принимайте, опираясь на характер запросов, темпы изменения схемы и требования к точности хранения, а при сомнениях проверяйте гипотезы на типичных для вашего приложения объёмах данных.

Источники

Это авторская статья, основанная на личном опыте и субъективном взгляде автора. Заметили ошибку или битую ссылку? Сообщите нам: info@codesrc.ru - мы оперативно исправим. Спасибо, что помогаете делать блог лучше.
Следите за нами в соцсетях:

Читайте также