JSONB против отдельных таблиц: что выбрать в PostgreSQL

от автора

в ,
Время чтения: 1 мин.

Практически в каждом проекте рано или поздно возникает вопрос:

Стоит ли хранить эти данные в JSONB или лучше создать отдельные таблицы?

Допустим, у товара появились характеристики.

Можно сделать так:

products

id
name
attributes jsonb
{
"color": "red",
"weight": 250,
"country": "Germany"
}

А можно сделать нормализованную структуру.

products
attributes
product_attributes

Какой вариант лучше?

Ответ не такой очевидный.


Когда JSONB действительно хорош

JSONB отлично подходит, если структура данных заранее неизвестна.

Например:

  • настройки пользователя;
  • параметры интеграций;
  • ответ внешнего API;
  • произвольные поля CRM;
  • данные маркетплейсов.

Например:

{
"ozon": {
"sku": 123,
"warehouse": 55
},
"wb": {
"barcode": "123456789"
}
}

Создавать десятки таблиц ради подобных данных обычно бессмысленно.


Когда JSONB начинает мешать

Представим, что нам нужно получить:

все товары красного цвета тяжелее 200 граммов.

Запрос выглядит так:

SELECT *
FROM products
WHERE
attributes->>'color' = 'red'
AND
(attributes->>'weight')::int > 200;

Работать это будет.

Но возникает сразу несколько вопросов.

  • Как индексировать?
  • Как обеспечить типизацию?
  • Как проверить обязательность поля?
  • Как сделать внешний ключ?
  • Как написать JOIN?

С каждым новым условием запрос становится сложнее.


Отдельные таблицы

Та же модель может выглядеть так.

products

product_specs

product_id
color
weight
country

Запрос становится обычным SQL.

SELECT *
FROM products p
JOIN product_specs s
ON s.product_id = p.id
WHERE
s.color = 'red'
AND
s.weight > 200;

Индексируется проще.

Планировщик понимает типы данных.

Работают ограничения.

Работают внешние ключи.


Производительность

Многие считают, что JSONB всегда быстрее.

Это неправда.

Все зависит от нагрузки.

JSONB выигрывает:

  • при редких выборках;
  • при неизвестной структуре;
  • при хранении документов.

Отдельные таблицы выигрывают:

  • при сложной аналитике;
  • при большом количестве JOIN;
  • при агрегировании;
  • при частой фильтрации.

Индексы

JSONB поддерживает GIN.

CREATE INDEX idx_products_attributes
ON products
USING gin(attributes);

Но это не означает, что любые запросы станут быстрыми.

Например

attributes @> '{"color":"red"}'

использует индекс.

А вот

(attributes->>'weight')::int > 200

обычно уже нет.

Приходится делать expression index.

CREATE INDEX idx_weight
ON products (((attributes->>'weight')::int));

Получается, что индекс становится привязан к конкретному полю.

Если таких полей десятки — преимуществ JSONB становится меньше.


Изменение схемы

Аргумент в пользу JSONB:

Не нужны миграции.

Это действительно удобно.

Сегодня записали:

{
"color": "red"
}

Завтра:

{
"color": "red",
"material": "steel"
}

Никаких ALTER TABLE.

Но есть обратная сторона.

Через год можно обнаружить, что часть записей содержит

weight

другая

Weight

третья

productWeight

И база уже никак это не контролирует.


Хороший компромисс

Во многих проектах отлично работает гибридная модель.

Основные поля:

id
name
price
weight
category_id

— отдельные колонки.

Редкие свойства:

{
"manufacturer_code": "...",
"custom_fields": ...
}

— JSONB.

Получается и быстро, и гибко.


Практическое правило

Можно пользоваться простой эвристикой.

Используйте JSONB, если:

  • структура заранее неизвестна;
  • поля редко участвуют в поиске;
  • данные приходят из внешних сервисов;
  • важна гибкость.

Используйте отдельные таблицы, если:

  • данные участвуют в JOIN;
  • по ним часто фильтруют;
  • нужны внешние ключи;
  • важна строгая схема;
  • требуется аналитика.

Итог

JSONB — мощный инструмент PostgreSQL, но он не заменяет реляционную модель.

Если поле становится частью бизнес-логики, по нему строятся индексы, выполняются фильтрации и соединения, его обычно стоит вынести в отдельную таблицу или колонку.

JSONB лучше рассматривать не как альтернативу нормализации, а как дополнение к ней. Лучшие схемы данных на практике часто сочетают оба подхода: реляционные таблицы для структурированных данных и JSONB для гибких, редко используемых или внешних атрибутов.


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Сколько будет 6 + 10?