PostgreSQL 的 jsonb

0 阅读5分钟

PostgreSQL 的 jsonb 是现代数据库领域的一个“杀手级”特性。它的出现让 Postgres 从一个单纯的关系型数据库(RDBMS),摇身一变成为了一个多模数据库(Multi-Model Database)

简单来说,jsonb 让你可以在关系型数据库里,获得不输给 MongoDB 的文档存储体验,同时还能享受 SQL 的强大功能。

以下是关于 jsonb 的详细解剖及其核心优势分析:


一、 什么是 jsonb?(vs json

Postgres 有两种 JSON 数据类型:jsonjsonb。虽然只差一个 "b",但底层天差地别。

  1. json (Text-based):

    • 存储方式:把 JSON 当作纯文本存储。
    • 特点:保留原始格式(包括空格、键的顺序、重复键)。
    • 缺点:每次查询时,数据库必须实时解析文本,无法建立高效的深层索引。
    • 用途:仅用于日志记录,或者你需要严格保留原始 JSON 格式的场景。
  2. jsonb (Binary decomposed):

    • 存储方式二进制格式。在写入时,PG 会把 JSON 解析并转换成一种树状的二进制结构。
    • 特点
      • 写入稍慢(因为有转换开销)。
      • 查询极快(不需要重新解析)。
      • 不保留格式(去除空格、去重键、不保证键的顺序)。
    • 核心能力支持索引(GIN Index)。这是它能打败竞品的关键。

二、 jsonb 相比竞品(MongoDB, MySQL)的优势

jsonb 的主要竞争对手是 MongoDB(NoSQL 代表)和 MySQL 的 JSON 类型

1. 对比 MongoDB:ACID 与 复杂查询的胜利

MongoDB 是原生的文档数据库,不仅存 JSON,还处理分片和高并发写入。但在很多业务场景下,Postgres jsonb 是更好的选择:

  • 最大的杀手锏:事务 ACID(Atomicity, Consistency, Isolation, Durability)

    • 场景:你要在电商系统里下一个订单,同时扣减库存(关系型表)并记录订单快照(JSONB)。
    • PG 优势:你可以把 UPDATE inventoryINSERT INTO orders (data) 放在同一个事务里。要么全成功,要么全失败。
    • MongoDB:虽然现在也支持多文档事务,但性能和复杂度的代价远高于 Postgres。在 Postgres 里这是原生、零成本的。
  • 关联查询(JOINs)的能力

    • PG 优势:你可以轻松地 JOIN 一个关系型表(如 User 表)和一个文档型列(如 Log 表里的 jsonb)。
    • MongoDB:虽然有 $lookup,但不仅写起来反人类,性能也无法与经过几十年优化的 SQL 优化器相比。
  • 运维成本(Stack Simplification)

    • 你不需要为了存一点非结构化数据就单独维护一个 MongoDB 集群。“One DB for All” 可以极大地降低运维复杂度。

2. 对比 MySQL JSON:索引与生态的碾压

MySQL 从 5.7 开始支持 JSON,但 Postgres 的 jsonb 在成熟度上依然领先:

  • 索引机制(GIN Index)

    • Postgres:使用 GIN (Generalized Inverted Index) 索引。你可以对整个 JSON 文档建立索引。
      • CREATE INDEX ON table USING GIN (data);
      • 查询 WHERE data @> '{"country": "CN"}' 时,它可以直接命中索引,不需要像 MySQL 那样必须为特定的 Key 创建虚拟列(Virtual Columns)或单独索引。
    • MySQL:虽然也可以索引,但通常需要通过提取生成的列(Generated Columns)来变相实现,灵活性和直接性不如 PG 的 GIN。
  • 操作符丰富度

    • Postgres 提供了极其丰富的 JSON 操作符:
      • @> (包含)
      • ? (键是否存在)
      • -> (获取元素)
      • #- (删除路径)
    • 这种语法糖让 SQL 写起来非常简洁。

三、 jsonb 的核心技术细节:为什么它这么快?

1. GIN 索引 (倒排索引)

这是 jsonb 性能的秘密武器。

假设你有一列 data 存了 { "tags": ["a", "b", "c"], "user": { "id": 1 } }。 当你建立 GIN 索引时,Postgres 会把里面的 Key 和 Value 拆解出来,建立一个倒排映射

  • "tags" -> 指向这一行
  • "a" -> 指向这一行
  • "b" -> 指向这一行

当你查询 WHERE data @> '{"tags": ["a"]}' 时,PG 不需要扫描整表,而是像搜索引擎查关键字一样,直接定位到包含 "a" 的行。这让千万级数据量的 JSON 查询可以在毫秒级完成。

2. jsonb_path_ops 优化

如果你只关心“包含关系”(比如查某个 Key 等于某个 Value),可以使用 jsonb_path_ops 索引类,它的体积比默认索引更小,速度更快。


四、 什么时候该用 jsonb?(最佳实践)

虽然 jsonb 很强,但不要把它当成偷懒的工具。不要把所有数据都塞进一个 JSON 字段里,那样你就失去了 SQL 强类型的优势。

推荐场景(Hybrid 混合模式):

  1. 稀疏属性 / EAV 模式的替代
    • 商品系统:不同的商品有不同的属性(衣服有颜色、尺码;电脑有 CPU、内存)。以前需要建立复杂的 EAV 表,现在直接一个 attributes 字段存 jsonb
  2. 不确定的外部数据
    • Webhook 接收:当你对接第三方支付或 API 时,对方返回的 JSON 结构可能会变。直接存 jsonb,后续再根据需要提取。
  3. 配置项 / Metadata
    • 用户的个性化设置(前端布局、通知开关),这些结构经常变,不适合频繁改表结构(DDL)。

不推荐场景:

  1. 高频更新的字段
    • jsonb 是作为一个整体存储的。如果你只修改 JSON 里的一小部分(例如 count++),PG 实际上是把整个旧 JSON 读出来,修改后,写一个新的 JSON 进去。这会产生大量的 WAL 日志和 IO 开销。如果某个字段频繁更新,请把它提出来做成独立的列。
  2. 核心业务外键
    • 尽量不要在 jsonb 内部存 user_id 然后指望用它去 JOIN。虽然技术上可行,但无法建立物理外键约束(Foreign Key Constraint),数据一致性难以保证。

总结

PostgreSQL 的 jsonb 是目前关系型数据库中对 JSON 支持最完美、性能最强悍的实现

  • 相比 MongoDB:它赢在事务一致性(ACID)、SQL 强大的分析能力以及运维的统一性。
  • 相比 MySQL:它赢在 GIN 索引的灵活性和对非结构化数据查询的极致优化。

如果你正在构建一个既需要事务安全,又需要灵活数据结构的系统(如 SaaS 平台、物联网后台、电商系统),PostgreSQL + jsonb (混合模式) 是目前的“版本答案”。