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多平台发布

相关推荐
Victor35617 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端
Victor35617 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
后端
灰子学技术19 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
Gogo81620 小时前
BigInt 与 Number 的爱恨情仇,为何大佬都劝你“能用 Number 就别用 BigInt”?
后端
fuquxiaoguang20 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
毕设源码_廖学姐20 小时前
计算机毕业设计springboot招聘系统网站 基于SpringBoot的在线人才对接平台 SpringBoot驱动的智能求职与招聘服务网
spring boot·后端·课程设计
野犬寒鸦1 天前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
逍遥德1 天前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
MX_93591 天前
Spring的bean工厂后处理器和Bean后处理器
java·后端·spring
程序员泠零澪回家种桔子1 天前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构