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

相关推荐
bcbnb3 小时前
被 4.3 拒绝的那些上架,从机制误判到工程治理的完整分析
后端
披荆斩棘的哥哥3 小时前
LOG:如何在Linux系统安装微软雅黑字体
后端
哈哈老师啊3 小时前
Springboot基于双减政策的家校互动管理系统8e613(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
程序员西西3 小时前
深入探索 Spring Boot3 中 Profiles 多环境配置
java·后端·架构
进击的丸子3 小时前
跨平台人脸识别 SDK 部署指南
linux·后端·代码规范
进击的丸子3 小时前
人脸识别项目如何在Spring Boot项目中如何建立数据库和管理
数据库·后端·mysql
爱上妖精的尾巴4 小时前
5-39 WPS JS宏 综合实例应用-4(多条件筛选记录并排序)
java·后端·restful·wps·js宏·jsa
廋到被风吹走4 小时前
【Spring】对多线程的支持
java·后端·spring
IT_陈寒4 小时前
Java并发编程避坑指南:这5个隐藏陷阱让你的性能暴跌50%!
前端·人工智能·后端