序列化协议怎么选?JSON、Protobuf、Avro 对比分析
一、什么是序列化
序列化就是把内存里的数据对象转成字节流或者字符串,方便存到硬盘或者通过网络发出去。反序列化就是反过来,把字节流再转回内存里的对象。
内存对象 → 序列化 → 字节流 → 网络传输或存盘
内存对象 ← 反序列化 ← 字节流序列化协议为什么重要?有三个原因:
性能影响:在高并发场景下,序列化和反序列化能占到CPU时间的30%到50%
带宽成本:序列化后的数据越大,花在网络传输上的钱就越多
开发效率:协议好不好用,直接影响开发改东西的速度
二、主流序列化协议对比
| 协议 | 类型 | 可读性 | 体积 | 速度 | 支持语言 |
|---|---|---|---|---|---|
| JSON | 文本 | 很好 | 大 | 慢 | 几乎所有语言 |
| XML | 文本 | 较好 | 最大 | 最慢 | 几乎所有语言 |
| Protobuf | 二进制 | 差 | 小 | 快 | 主流语言 |
| MessagePack | 二进制 | 差 | 中等 | 较快 | 几乎所有语言 |
| Thrift | 二进制 | 差 | 小 | 快 | 主流语言 |
| Avro | 二进制 | 差 | 最小 | 快 | 主要是JVM生态 |
三、JSON:简单但成本高
JSON的设计目标就是简单、人类可读、通用。下面是一个例子:
{
"userId": 10001,
"userName": "张三",
"age": 28,
"hobbies": ["编程", "阅读"],
"createTime": "2024-01-15T10:30:00Z"
}JSON有三个主要性能问题。
第一个问题:文本编码太占空间
JSON用UTF-8文本编码,每个数字字符占1个字节。比如数字12345,JSON里要存5个字节('1','2','3','4','5')。如果换成二进制存,int32只要4个字节,int64只要8个字节。数字越大,浪费越多。
第二个问题:解析过程复杂
JSON解析要经过词法分析、语法分析、建抽象语法树、类型推断转换这些步骤。每个字符都要做边界检查、ASCII转换、累乘运算、溢出检测,开销不小。
第三个问题:没有Schema,得用反射
JSON没有预先定义的数据结构,反序列化的时候得用反射来判断类型。反射本身就有性能开销,还要做字段名字符串匹配和类型检查。
JSON什么时候用
适合做公开API、第三方对接、配置文件、日志输出、前后端通信。不适合做高性能内部服务调用、大数据量传输、实时性要求高的场景。
四、Protobuf:Google的工业标准
Protobuf是Google开源的序列化框架,性能好主要靠三个设计。
第一个设计:二进制加Varints变长整数
Varints编码用每个字节的最高位表示有没有后续字节。1表示还有下一个,0表示这是最后一个。
小数字在Protobuf里只占1个字节,JSON要占1到3个字节。数值越大,Protobuf省得越多。
第二个设计:Schema预编译
Protobuf要先写.proto文件定义数据结构:
syntax = "proto3";
message User {
int32 user_id = 1;
string user_name = 2;
int32 age = 3;
repeated string hobbies = 4;
int64 create_time = 5;
}预编译有两个好处:字段名不传输,只传字段编号;生成强类型代码,不用反射,直接写内存。
第三个设计:TLV编码
每个字段按Tag-Length-Value的格式存。Tag里包含了字段编号和类型信息。
Protobuf什么时候用
适合微服务内部通信、gRPC、高频RPC调用、游戏实时数据同步、移动端API。不适合公开API、需要人眼可读的场景、前端直接用的数据。
五、其他序列化协议
MessagePack
可以理解成JSON的二进制版,保持了JSON的灵活性,但通过二进制编码把体积压缩了。比如一个27字节的JSON,转成MessagePack大约18字节。
适合需要JSON灵活性但想要更好性能的场景,也适合Redis存储优化。
Apache Thrift
Facebook开发,支持多种序列化协议。二进制协议适合生产环境,压缩协议适合带宽紧张的场景,JSON协议方便调试。
适合大数据生态里的Hadoop、HBase,也适合跨语言RPC服务。
Apache Avro
核心设计是Schema和数据分离。序列化后的数据只存值,不存字段信息,所以体积最小。Schema单独存放,改字段规则时新老数据都能兼容。
适合大数据存储里的Hadoop、Hive,还有数据湖、流式数据处理。
六、选型决策基础
选序列化协议主要看三点:数据要不要给人看、性能要求高不高、跨语言场景多不多。
需要人看或者对外提供接口,选JSON或YAML。追求极致性能且内部服务用,选Protobuf。做大数据存储且Schema经常变,选Avro。既要灵活性又想比JSON强一点,试试MessagePack。
七、不同场景怎么选
微服务内部通信:Protobuf
配合gRPC用,自动生成客户端代码,类型安全,性能高。
对外公开API:JSON
开发者不用额外装工具就能用,调试方便,生态丰富。
大数据存储:Avro加Parquet
Avro做序列化,Parquet做列存,压缩好,查询快。
移动端网络通信:Protobuf
体积小省流量,解析快省电。可以再用sint32编负数、packed编数组这些技巧进一步优化。
八、总结
下面是快速选型参考:
| 场景 | 推荐协议 | 原因 |
|---|---|---|
| 微服务内部 | Protobuf | 高性能、类型安全、有代码生成 |
| 公开API | JSON | 通用、生态好、方便调试 |
| 配置文件 | YAML | 可读性好、支持注释 |
| 大数据存储 | Avro | Schema能演进、对列存友好 |
| 游戏实时通信 | Protobuf | 延迟低、体积小 |
| 移动端API | Protobuf | 省流量、解析快 |
记住几条原则:不要一味追求性能,JSON在大部分场景下已经够用了。要考虑团队的学习成本,Protobuf需要多学一套工具链。可以混着用,对外给JSON,内部跑Protobuf。最后一定要在自己真实场景里跑一跑压测,别人的数据只能做参考。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!