0x00 概述
先说结论:不是,不同的场景有不同的最佳选择。
介于json良好的可读性和适中的时空性能,在多数协议设计和信息缓存时候被无脑选择使用,对于性能不敏感的业务场景而言,这完全没有问题。 但是一些对于性能敏感的场景,或者内存缓存场景而言,json的选用则需要谨慎的评估考量。
接下来我们从通用数据到实例测试来看一下几种数据格式的差异对比。
0x01核心特性对比
1. MessagePack (msgp)
- 类型: 二进制JSON
- Schema: 无需schema
- 特点: 比JSON更紧凑的二进制格式
2. Protocol Buffers (protobuf)
- 类型: 二进制序列化
- Schema: 需要.proto文件定义
- 特点: Google出品,成熟稳定
3. FlatBuffers
- 类型: 零拷贝二进制
- Schema: 需要.fbs文件定义
- 特点: 无需反序列化即可访问
4. Simple Binary Encoding (SBE)
- 类型: 零拷贝二进制
- Schema: 需要XML schema
- 特点: 为金融交易设计,极致性能
5. JSON
- 类型: 文本格式
- Schema: 无需schema
- 特点: 人类可读,广泛支持
0x02 性能对比
序列化速度排名
1. SBE ⚡⚡⚡⚡⚡ (最快,通常<100ns)
2. FlatBuffers ⚡⚡⚡⚡ (接近SBE,零拷贝)
3. msgp ⚡⚡⚡ (比JSON快5-10倍)
4. Protobuf ⚡⚡⚡ (与msgp相近)
5. JSON ⚡ (最慢,需要字符串解析)
反序列化速度排名
1. FlatBuffers ⚡⚡⚡⚡⚡ (零拷贝,无需反序列化)
2. SBE ⚡⚡⚡⚡⚡ (零拷贝)
3. msgp ⚡⚡⚡
4. Protobuf ⚡⚡
5. JSON ⚡
实测数据参考(1MB数据):
- SBE: 序列化 ~0.05ms,反序列化 ~0.01ms
- FlatBuffers: 序列化 ~0.1ms,反序列化 ~0.001ms
- msgp: 序列化 ~1ms,反序列化 ~2ms
- Protobuf: 序列化 ~2ms,反序列化 ~3ms
- JSON: 序列化 ~10ms,反序列化 ~15ms
0x03 空间成本对比
以一个典型的用户对象为例:
json
{
"id": 123456789,
"name": "张三",
"email": "zhangsan@example.com",
"age": 28,
"balance": 1234.56,
"tags": ["vip", "active"]
}
数据大小对比
JSON: ~150 bytes (100%)
msgp: ~95 bytes (63%)
Protobuf: ~85 bytes (57%)
FlatBuffers: ~120 bytes (80%) - 包含偏移表
SBE: ~75 bytes (50%)
压缩效率
格式 原始 +gzip +zstd
JSON 100% 40% 35%
msgp 63% 45% 40%
Protobuf 57% 42% 38%
FlatBuffers 80% 55% 50%
SBE 50% 43% 39%
四、详细优劣势分析
SBE (Simple Binary Encoding)
✅ 优势:
- 极致的性能,专为低延迟设计
- 零拷贝,零内存分配
- 可预测的内存布局
- CPU缓存友好
❌ 劣势:
- 学习曲线陡峭
- 生态系统较小
- Schema演进困难
- 主要支持C++/Java
🎯 适用场景: 高频交易、实时竞价、游戏服务器
FlatBuffers
✅ 优势:
- 零拷贝访问,无需完整反序列化
- 内存高效
- 支持随机访问
- 向后/向前兼容性好
❌ 劣势:
- 序列化后体积略大(因为偏移表)
- 写入性能略低于读取
- API相对复杂
🎯 适用场景: 游戏开发、移动应用、嵌入式系统
MessagePack (msgp)
✅ 优势:
- 无需schema,开发快速
- 比JSON小且快
- 多语言支持好
- 易于调试(与JSON类似)
❌ 劣势:
- 性能不如schema-based方案
- 无类型安全
- 无版本控制机制
🎯 适用场景: 微服务通信、缓存、日志存储
Protocol Buffers
✅ 优势:
- 成熟稳定,生态系统完善
- 强类型安全
- 优秀的向后兼容性
- gRPC原生支持
❌ 劣势:
- 需要代码生成
- 反序列化需要完整解析
- 性能不如SBE/FlatBuffers
🎯 适用场景: 微服务RPC、API通信、数据存储
JSON
✅ 优势:
- 人类可读,易于调试
- 无需工具链
- 浏览器原生支持
- 生态系统最完善
❌ 劣势:
- 体积大(字段名重复)
- 解析慢(字符串处理)
- 类型不安全("1" vs 1)
- CPU和内存开销大
🎯 适用场景: Web API、配置文件、开发调试
0x04 实测对比
结合后端批量数据缓存需求,实测几种协议格式的解码性能和缓存与传输空间消耗。
测试结果如下:
================================ Wire Decode Benchmark ================================
Date: 2025-10-15T13:14:40+08:00 | Go: go1.25.2 | GOMAXPROCS=14 | N=1000 items
---------------------------------------------------------------------------------------
Payload Sizes:
- msgp : total= 614594 B avg=614.6 B/item gzip= 37510 B
- protobuf : total= 437594 B avg=437.6 B/item gzip= 34043 B
- flatbuffers: total= 621164 B avg=621.2 B/item gzip= 41521 B
- sbe : total= 468594 B avg=468.6 B/item gzip= 35724 B
- json : total= 710594 B avg=710.6 B/item gzip= 39157 B
---------------------------------------------------------------------------------------
Codec Decode once (ns/item) Decode x1000 (ns/item) Note
flatbuffers 374.6 363.1
sbe 478.3 458.2
msgp 515.4 274.0
protobuf 577.0 558.5
json 4616.1 4492.7
---------------------------------------------------------------------------------------
* Decode once:每个元素只解码 1 次;Decode x1000:每个元素在同一字节切片上重复解码 1000 次(考察热点路径/复用缓存的吞吐上限)。
* gzip 为整包压缩后的大小,仅作带宽估算参考。
=======================================================================================
很显然能看到几个情况:
- flatbuffers的解码性能是最高的,当然空间占用也相对较大,仅次于json。
- 无论协议格式是什么类型,在传输层的表现差异不明显,因为gzip之后大小都差不多。
0x05 小结
JSON绝不是最优解,它只是在开发便利性和广泛支持上占优。选择序列化协议应该基于:
- 性能要求: 延迟<1ms → SBE/FlatBuffers
- 开发效率: 快速迭代 → JSON/msgp
- 兼容性: 长期存储 → Protobuf
- 生态系统: 多语言 → Protobuf/JSON
- 数据规模: 大数据量 → 二进制格式
黄金法则: 对外JSON保持兼容,对内二进制追求性能。
实际生产中我们应该结合需要采用混合方案,例如:
- 外部API (JSON)
- API Gateway (JSON)
- 内部RPC (Protobuf/gRPC)
- 缓存层 (msgp/FlatBuffers)
- 热路径优化/极致性能优化 (SBE/零拷贝)
- 持久化 (Database/Protobuf)