Что такое JSONB в PostgreSQL?

MiddlePostgreSQL · Backend·Обновлено 5 июля 2026
Коротко
JSONB — это бинарный формат хранения JSON-данных в PostgreSQL, который сохраняет данные в разобранном виде, обеспечивая быстрый поиск и поддержку индексов GIN/GiST. В отличие от типа JSON, JSONB не сохраняет оригинальное форматирование и порядок ключей, но значительно выигрывает в производительности при запросах.

JSONB в PostgreSQL

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

Отличия JSON vs JSONB

Характеристика json jsonb
Хранение Текст «как есть» Бинарный разобранный формат
Запись Быстрее Медленнее (разбор при вставке)
Чтение/поиск Медленнее (разбор при каждом чтении) Быстрее
Порядок ключей Сохраняется Не гарантируется
Дублирующиеся ключи Сохраняются все Побеждает последний
Индексы GIN/GiST Нет Да
Поддержка операторов @>, <@, ?, ?&, ? Нет Да

Когда использовать JSONB

JSONB предпочтителен в большинстве случаев, когда:

  • нужен поиск по содержимому документа;
  • данные читаются чаще, чем пишутся;
  • требуется индексирование JSON-полей;
  • важна производительность запросов.

Тип json оправдан лишь когда критично сохранить оригинальное форматирование или порядок ключей (например, для аудита).

Операторы JSONB

-- Проверка вхождения: содержит ли документ указанный фрагмент
SELECT * FROM users WHERE profile @> '{"role": "admin"}';

-- Проверка существования ключа
SELECT * FROM users WHERE profile ? 'email';

-- Проверка существования любого из ключей
SELECT * FROM users WHERE profile ?| ARRAY['email', 'phone'];

-- Проверка существования всех ключей
SELECT * FROM users WHERE profile ?& ARRAY['email', 'name'];

Индексирование JSONB

Главное преимущество JSONB — возможность создания GIN-индекса, который покрывает все ключи и значения документа:

-- Создание GIN-индекса по всему JSONB-полю
CREATE INDEX idx_users_profile ON users USING GIN (profile);

-- Индекс по конкретному пути (более компактный)
CREATE INDEX idx_users_role ON users USING BTREE ((profile->>'role'));

-- jsonb_path_ops — более компактный GIN-индекс, только для @>
CREATE INDEX idx_users_profile_path ON users USING GIN (profile jsonb_path_ops);

Работа с вложенными данными

-- Оператор -> возвращает JSONB
-- Оператор ->> возвращает текст
SELECT
  profile -> 'address' AS address_json,
  profile ->> 'name'   AS name_text,
  profile -> 'address' ->> 'city' AS city
FROM users;

-- Обновление отдельного ключа без замены всего документа
UPDATE users
SET profile = jsonb_set(profile, '{address, city}', '"Москва"')
WHERE id = 1;

-- Удаление ключа
UPDATE users
SET profile = profile - 'temp_field'
WHERE id = 1;

JSONPath (PostgreSQL 12+)

-- Поиск с использованием JSONPath
SELECT * FROM orders
WHERE jsonb_path_exists(data, '$.items[*] ? (@.price > 1000)');

Практические советы

  • Не злоупотребляйте JSONB для данных с фиксированной структурой — реляционная схема обычно эффективнее.
  • GIN-индекс ускоряет операторы @>, ?, ?|, ?&, но не операторы -> и ->>.
  • Для частых запросов по конкретному полю лучше создать выражённый B-tree индекс ((data->>'field')).
  • Функция jsonb_pretty() удобна для отладки.

Что хочет услышать интервьюер

Понимание разницы между json и jsonb: бинарное хранение, скорость чтения vs записи, порядок ключей

Знание специфических операторов JSONB: @>, ?, ?|, ?&, ->, ->>

Понимание индексирования: GIN-индекс и его роль в производительности запросов

Умение обновлять отдельные поля через jsonb_set без перезаписи всего документа

Осознание границ применимости: когда JSONB оправдан, а когда лучше нормализованная схема

Пример: Создание таблицы с JSONB и базовые операции

-- Создание таблицы с JSONB-полем
CREATE TABLE products (
  id   SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  meta JSONB
);

-- Вставка данных
INSERT INTO products (name, meta) VALUES
  ('Ноутбук', '{"brand": "Apple", "specs": {"ram": 16, "ssd": 512}, "tags": ["pro", "m3"]}'),
  ('Мышь',    '{"brand": "Logitech", "specs": {"dpi": 4000}, "tags": ["wireless"]}');

-- Получить значение поля как текст
SELECT name, meta->>'brand' AS brand FROM products;

-- Найти все товары бренда Apple
SELECT * FROM products WHERE meta @> '{"brand": "Apple"}';

-- Найти товары с тегом 'wireless'
SELECT * FROM products WHERE meta @> '{"tags": ["wireless"]}';

-- GIN-индекс для ускорения поиска
CREATE INDEX idx_products_meta ON products USING GIN (meta);

-- Обновить только одно поле внутри JSONB
UPDATE products
SET meta = jsonb_set(meta, '{specs, ram}', '32')
WHERE id = 1;

-- Удалить ключ из JSONB
UPDATE products
SET meta = meta - 'tags'
WHERE id = 2;

Типичные ошибки

Путают операторы -> (возвращает JSONB) и ->> (возвращает text), что приводит к ошибкам сравнения типов

Забывают создать GIN-индекс и удивляются медленной работе оператора @> на больших таблицах

Используют JSONB там, где данные имеют стабильную структуру — теряют преимущества реляционной модели

Полностью перезаписывают весь JSON при обновлении одного поля вместо использования jsonb_set

Не учитывают, что JSONB не сохраняет порядок ключей и удаляет дублирующиеся ключи

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее