jsonb 用起来很方便,加字段不用改表结构。但什么都往里放,后面查询和维护都会很痛苦。
适合放进 jsonb 的
- 结构经常变化、只按主键整体读写的扩展属性,比如从旧系统迁移过来的元数据。
- 第三方回调、Webhook 的原始数据,留档备查。
- 用户的个性化设置这类“读出来整体用”的配置。
应该单独建列的
- 经常出现在 WHERE、ORDER BY 里的字段。
- 需要外键、唯一约束或 NOT NULL 约束的字段。
- 金额、状态这类出错代价很高的字段。
常用写法
SQL
-- ->> 取出 text,-> 取出 jsonb
SELECT id, meta ->> 'source' AS source
FROM posts
WHERE meta @> '{"featured": true}';
-- 包含查询(@>)用 GIN 索引
CREATE INDEX posts_meta_gin ON posts USING gin (meta jsonb_path_ops);
-- 某个键经常单独筛选时,表达式索引更小更快
CREATE INDEX posts_meta_source_idx ON posts ((meta ->> 'source'));
-- 合并更新,不必把整段 JSON 读出来再写回
UPDATE posts
SET meta = coalesce(meta, '{}'::jsonb) || '{"featured": true}'
WHERE id = 42;jsonb_path_ops 只支持 @> 这类包含查询,但索引比默认的 jsonb_ops 小不少。一个经验:某个键开始频繁出现在查询条件里,就该把它提升成普通列了。