PostgreSQL 实践:JSON vs JSONB

------ 为什么 99% 的场景使用 JSONB?

摘要: 随着 PostgreSQL 成为新一代"全能型数据库",JSON 支持也让它具备了替代 MongoDB 的能力。但在建表时,你是否也纠结过------字段到底该用 json,还是 jsonb?两者虽然都能存 JSON,但底层结构、性能表现、索引能力却完全不同。 选错类型,查询性能可能慢几十倍。 本文将从底层机制、性能、索引、应用场景和 Spring Boot 实战全面解析两者的差异,并给出最稳的选型建议。


01|为什么 PostgreSQL 有两种 JSON?

不同于 MySQL,PostgreSQL 提供:

  • ** json**:文本 JSON
  • ** jsonb**:二进制 JSON(推荐)

它们都能存储合法 JSON,但核心目标完全不同。

一句话总结:

  • json:原样存储,格式完全保留
  • jsonb:性能与索引优先,结构化存储

02|JSON vs JSONB:底层差异

🔹 JSON:只是"被验证过的字符串"

特点:

  • 按文本原样存储(空格、换行、缩进都会保留)
  • 保留键顺序、保留重复键
  • 读取时必须重新解析 → 查询性能很差

适合:只保存不查询的原始报文、审计日志。


🔹 JSONB:结构化的二进制存储(性能王者)

特点:

  • 转为二进制结构后存储(紧凑占用更小)
  • 自动去重键(只保留最后一个)
  • 键顺序会被重新排序
  • 支持索引(GIN)
  • 查询无需解析 → 非常快

查询性能可比 JSON 快数十倍。


03|一张表看懂差别

维度 JSON JSONB(推荐)
写入速度 稍慢
读取速度 ❌ 慢 ✔ 非常快
查询能力 ✔ 强
是否支持 GIN 索引 ❌ 否 ✔ 是
空间占用
支持局部更新 较弱 ✔ 强大(jsonb_set)
保留格式 ✔ 完整保留 ❌ 不保留

如果你需要查询 JSON 内容 → 必须使用 JSONB。


04|什么时候应该用 JSON?(非常少)

必须同时满足:

  1. 你不会查询 JSON 内部字段;
  2. 你需要保留原始格式(顺序、空格、换行);
  3. 你非常在意写入速度;

典型场景:

  • 原始日志存档
  • 审计数据快照
  • 协议报文的字节级保留

05|什么时候应该用 JSONB?(>99% 的业务)

出现以下任意一点 → 选 JSONB:

  • 需要查询 JSON 内部字段
  • 需要索引内部字段
  • JSON 内容会变化
  • 需要局部更新某个字段
  • 典型半结构化数据

常见业务:

  • 即时通讯消息体(读取状态、上传状态等)
  • 电商商品属性(颜色、规格等)
  • SaaS 动态设置(用户配置项)
  • IoT 设备上报数据

06|实战:PostgreSQL 表与索引

🔹 建表推荐:

复制代码
CREATE TABLE wx_message (
    id BIGINT PRIMARY KEY,
    payload JSONB
);

🔹 GIN 索引(性能飞升关键)

复制代码
CREATE INDEX idx_wx_message_payload 
ON wx_message 
USING GIN (payload);

没有 GIN 索引,JSONB 实力只发挥一半。


07|Spring Boot / Hibernate 最佳实践

🔹 Entity 映射

复制代码
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
private String payload;

为什么用 String

  • 避免复杂的 JSON 多态反序列化
  • 保证结构灵活
  • 与前端自由 JSON 协议更匹配

🔹 JSONB 局部更新(非常关键)

例如更新消息的 mediaStatus

复制代码
UPDATE wx_message
SET payload = jsonb_set(payload, '{mediaStatus}', to_jsonb('uploaded'), true)
WHERE id = 123;

优点:

  • 原子更新,不需要查出来再写回
  • 不会发生并发覆盖
  • 比读取->反序列化->再保存快得多

Spring JPA 版本:

复制代码
@Modifying
@Transactional
@Query("""
    UPDATE wx_message
    SET payload = jsonb_set(payload, '{mediaStatus}', to_jsonb(:status), true)
    WHERE session_id = :sessionId AND wxid = :messageId
""")
int updateMediaStatus(Long sessionId, String messageId, String status);

08|总结:选型建议

✔ 默认选 JSONB

  • 查询快
  • 索引强
  • 占用小
  • 支持局部更新
  • 满足绝大多数实际业务需求

❗仅在以下场景用 JSON:

  • 必须保留原始格式(日志、协议原文)
  • JSON 不参与任何查询

本文由mdnice多平台发布

相关推荐
一线大码19 分钟前
SpringBoot 3 和 4 的版本新特性和升级要点
java·spring boot·后端
weixin_4250230035 分钟前
Spring Boot 配置文件优先级详解
spring boot·后端·python
weixin_4250230036 分钟前
Spring Boot 实用核心技巧汇总:日期格式化、线程管控、MCP服务、AOP进阶等
java·spring boot·后端
一线大码42 分钟前
Java 8-25 各个版本新特性总结
java·后端
VX:Fegn08951 小时前
计算机毕业设计|基于springboot + vue校园社团管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
To Be Clean Coder1 小时前
【Spring源码】通过 Bean 工厂获取 Bean 的过程
java·后端·spring
weixin199701080162 小时前
闲鱼 item_get - 商品详情接口对接全攻略:从入门到精通
java·后端·spring
自己的九又四分之三站台3 小时前
导入数据到OG GraphQL以及创建graph
java·后端·graphql
+VX:Fegn08954 小时前
计算机毕业设计|基于springboot + vueOA工程项目管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
爱上妖精的尾巴5 小时前
7-3 WPS JS宏 keys、values、entries、JSON.stringify 循环对象中的属性
后端·restful·wps·jsa