Короткий ответ
JSONB уместен для действительно полуструктурированных или часто меняющихся по составу данных, когда заранее неизвестна или нестабильна схема, а не как универсальная замена нормализованных таблиц; для ускорения поиска по ключам/значениям внутри JSONB создают GIN-индекс.
Как это работает подробнее
JSONB хранит данные в бинарном разложенном виде, что ускоряет выборочный доступ к ключам и позволяет строить GIN-индекс, поддерживающий операторы @>, ?, ?& и поиск по пути jsonb_path_ops.
Пример кода
example.sql
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
attributes JSONB NOT NULL DEFAULT '{}'::jsonb
);
CREATE INDEX idx_products_attributes ON products USING GIN (attributes);
-- Найти товары с атрибутом color = "red"
SELECT * FROM products WHERE attributes @> '{"color": "red"}';
EXPLAIN SELECT * FROM products WHERE attributes @> '{"color": "red"}';
-- При наличии GIN-индекса план покажет Bitmap Index Scan вместо полного Seq ScanЧто использовать на практике
- Практика. Типичный баланс — вынести стабильные, часто фильтруемые атрибуты в обычные колонки/таблицы, а вариативные или редко запрашиваемые — в JSONB; злоупотребление JSONB усложняет валидацию, JOIN и миграции данных внутри самого JSON.
Подводные камни
- Ошибка. Хранят в JSONB данные, которые по сути реляционные и регулярно участвуют в JOIN/агрегации, что затрудняет аналитические запросы; создают обычный B-tree индекс на JSONB-колонку целиком вместо GIN-индекса.