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

相关推荐
明月_清风17 分钟前
FastAPI 从入门到实战:3 分钟构建高性能异步 API
后端·python·fastapi
小村儿18 分钟前
连载10-Sub-agents 深度解析:从源码理解 Claude Code 的分身术
前端·后端·ai编程
他们叫我阿冠19 分钟前
Day5学习--SpringBoot详解
spring boot·后端·学习
枕星而眠1 小时前
Linux 四大进程/线程同步锁详解:互斥锁、读写锁、条件变量、文件锁
linux·c语言·后端·ubuntu·学习方法
IT_陈寒1 小时前
Vite动态导入把我坑惨了,原来要这样用才对
前端·人工智能·后端
计算机魔术师3 小时前
【AI面试八股文 Vol.3.4:训练微调部署选型】从预训练到量化部署:LLM 工程落地如何做模型选择
人工智能·后端·面试·架构·moe·vol.3.3·vol.3.4
明月_清风3 小时前
从零到一构建生产级 AI Agent:架构拆解 × Python 高并发实战 × 技术选型方法论
后端·agent
金銀銅鐵4 小时前
[Java] 如何理解 class 文件中字段的 access flags?
java·后端
不懒不懒4 小时前
基于 Flask —— 异步任务处理接口服务
后端·python·flask
Xidaoapi4 小时前
Python FastAPI性能优化实战:8个让你的API快3倍的技巧
后端·程序员