jsonb 与 json 是什么
PostgreSQL 提供两种 JSON 数据类型。json 保存原始文本,写入时不解析,读取时每次都要重新解析;jsonb 以二进制形式存储,写入时解析并规范化(去掉多余空白、合并重复键),支持 GIN 索引和完整的操作符集合。选型的默认答案是新表一律用 jsonb,只有需要逐字保留原始报文(留档、审计、键顺序敏感)时才用 json。两者之间没有隐式转换,混用时必须显式 ::jsonb 或 ::json。
基本信息
| 存储形式 | json:原始文本;jsonb:解析后的二进制 |
|---|---|
| 写入成本 | json 低(不解析);jsonb 高(需解析与去重) |
| 读取与查询 | jsonb 明显更快,且支持 @> ? ?| @? @@ 等操作符 |
| 索引支持 | json 无原生 GIN 支持;jsonb 可用 GIN(jsonb_ops / jsonb_path_ops) |
| 重复键 | json 原样保留;jsonb 只保留最后一个 |
典型使用场景
- 需要按 key 查询、做包含判断的表:用 jsonb,才能建 GIN 索引加速
- 原始报文留档、审计留痕:用 json,保证与上游系统字节级一致
- 作为可变配置载体:用 jsonb,读取频繁且值小,解析开销可接受
- 几乎只整段读写的场景:json 写入更快,但要接受每次读取重新解析
- 旧表从 json 迁移到 jsonb:一次性 ALTER 转换,成本集中在写入规范化
建表与索引
建表与索引
CREATE TABLE events (
id bigserial PRIMARY KEY,
payload jsonb NOT NULL DEFAULT '{}'::jsonb # 需要检索就选 jsonb
);
CREATE INDEX idx_events_payload
ON events USING gin (payload jsonb_path_ops); # 支持 @> 包含查询加速
CREATE INDEX idx_events_status
ON events ((payload->>'status')); # 表达式索引也能用于取文本后过滤类型转换与查询
类型转换与查询
# json 转 jsonb:会规范化内容,去重复键与多余空白
ALTER TABLE events ALTER COLUMN payload TYPE jsonb USING payload::jsonb;
SELECT payload->>'status' AS status # ->> 返回 text,可直接比较
FROM events
WHERE payload @> '{"type": "pay"}'::jsonb; # jsonb 才走 GIN 索引
SELECT count(*) FROM events WHERE payload ? 'refund'; # 键是否存在,仅 jsonb 支持选型对照
| 对比项 | json | jsonb |
|---|---|---|
| 存储 | 原始文本,空格与键顺序保持一致 | 二进制,去空白、键去重 |
| GIN 索引 | 不支持 | 支持 jsonb_ops / jsonb_path_ops |
| 常用操作符 | -> ->> 等少量 | @> ? ?| ?& @? @@ 完整支持 |
| 写入性能 | 快 | 慢,需要解析与排序 |
| 适用判断 | 保真留档、几乎不查询 | 默认选择,需要检索与索引 |
jsonb 输出时键顺序可能被重排,老版本与新版本表现也不完全一致;对键顺序有硬性要求的场景应使用 json,或在应用层自行排序后再比较。
json 与 jsonb 没有隐式转换,函数传错类型会报 operator does not exist;排查这类错误的顺序是先 \d 表名 看列类型,再对上函数签名。