该用 jsonb 而不是 text[] 当需按标签属性(如 status=active)做路径查询,因 jsonb 支持 @>、-> 等操作及 gin jsonb_path_ops 索引;text[] 仅适用于结构固定、只做整体成员判断的扁平字符串列表。

什么时候该用 jsonb 而不是 text[] 存标签列表
数组类型 text[] 适合结构固定、只做“包含/不包含”判断的扁平列表,比如用户权限集合 {'read', 'write'};但一旦要查“带某个属性的标签”,比如“所有 status=active 的标签”,数组就无能为力。jsonb 支持路径查询和索引(GIN 索引 + jsonb_path_ops),能高效执行 WHERE data @> '{"status":"active"}'。常见错误是把 JSON 当字符串存进 text 字段,结果连 @> 都用不了——必须用 jsonb 类型,且插入时别漏掉 ::jsonb 强转。
- 数组不能按字段内容检索,
jsonb可以用->、->>、@>做任意层级过滤 -
jsonb写入稍慢(解析+规范化),但读取和索引效率远超hstore或拼接字符串 - 如果只是存“一组不可再分的字符串”,且永远只用
ANY或@>查整体成员,text[]更轻量、更省内存
hstore 还值得考虑吗?哪些场景它没被 jsonb 完全替代
hstore 在 PostgreSQL 9.6+ 已基本被 jsonb 降维打击,唯一还留有一席之地的是“纯键值对 + 高频更新单个字段”的 OLTP 场景。因为 hstore 更新单个 key 是原地修改(hstore || hstore),而 jsonb 每次 jsonb_set() 都要重写整个值,WAL 日志更大、MVCC 开销更高。但代价是:不支持嵌套、无法建 GIN 路径索引、聚合函数少。如果你的业务真在每秒更新上万次单个 key,且确定永远不需要嵌套或数组,hstore 仍比 jsonb 实在。
- 错误现象:
UPDATE t SET props = props || 'a=>1' WHERE id=1在jsonb里得写成jsonb_set(props, '{a}', '1'::jsonb),更啰嗦且更重 -
hstore不支持NULL值(空字符串 ≠ NULL),jsonb支持完整 JSON 语义 - PostgreSQL 14+ 对
jsonb的部分更新做了优化,但高频小字段更新仍是hstore的舒适区
为什么 jsonb 的 GIN 索引有时查不出数据
最常见原因是用了默认的 jsonb_ops 索引,它只加速 @>、、<code>?、?|、?& 这几个操作符,对 -> 或 ->> 提取后比较(如 (data->>'type') = 'user')完全无效。必须显式建 jsonb_path_ops 索引才能支持路径表达式查询,但代价是索引体积更大、不支持 ? 类操作。另一个坑是:jsonb 会自动去重键、排序字段,导致 jsonb '{"b":1,"a":2}' = '{"a":2,"b":1}' 为 true,但如果你依赖字段顺序(比如前端靠顺序渲染),就得自己处理。
- 建索引前先确认查询模式:
CREATE INDEX idx_data_type ON t USING GIN (data jsonb_path_ops) -
jsonb中数字1和字符串"1"类型不同,WHERE data @> '{"count":1}'查不到"count":"1" - 用
EXPLAIN看是否真走了索引——有时候 planner 觉得全表扫更快,尤其小表
从 text[] 或 hstore 迁移到 jsonb 的实际卡点
迁移本身不难,但容易忽略语义断层。比如 hstore 的 akeys() 返回 text 数组,直接转 jsonb 会丢 key 的顺序;text[] 里 '{a,b,c}' 转成 jsonb 得先包装成对象或数组,否则解析失败。更隐蔽的是:应用层可能把 hstore 当字典用,直接 props['key'] 访问,而 jsonb 必须显式调用 -> 或 ->>,ORM 层若没适配好,运行时报错是 operator does not exist: jsonb = text 这类模糊提示。
- 批量转换示例:
ALTER TABLE t ADD COLUMN data jsonb DEFAULT '{}'::jsonb;,然后UPDATE t SET data = to_jsonb(hstore_to_array(props)) WHERE props IS NOT NULL; -
hstore的NULL值转jsonb会变成缺失字段,不是null,前端 JS 解构可能出错 - 迁移后务必检查所有 WHERE 条件、ORDER BY 表达式、聚合函数(
jsonb_object_keys()替代akeys())
jsonb 的 MVCC 开销,以及愿不愿意让查询逻辑从 SQL 层下沉到应用层做 schema 校验。










