4、序列化与反序列化:JSON、Protobuf、Avro、FlatBuffers的性能对比与选型
聊到实时数据流,序列化是个绕不开的话题。说白了,就是把内存里的对象变成一串字节,通过网络发出去;另一端再把这串字节变回对象。这个过程快不快,直接影响端到端的延迟。
我见过不少团队,一上来就用 JSON,图它简单。结果数据量一上来,CPU 被打满,GC 频繁,延迟直接飙到几百毫秒。嗯,这时候才想起来换方案,代价就大了。
今天咱们就把主流的四种序列化方案——JSON、Protobuf、Avro、FlatBuffers——掰开揉碎,看看它们到底差在哪,什么时候该用谁。
先看一张全景图
下面这张图,是我自己总结的选型决策流程。你跟着它走,基本不会选错。
JSON:最熟悉,但最慢
JSON 的好处不用我多说——人类可读,调试方便。但它的性能,说实话,在实时流里有点拖后腿。
为什么慢?三个原因:
- 文本解析开销大:每个字段都要做字符串匹配、类型转换
- 没有 Schema:字段名每次都完整传输,冗余严重
- 内存占用高:解析后生成的对象结构松散,GC 压力大
我做过一个压测:同样的 10 万条数据,JSON 序列化耗时 320ms,反序列化 410ms。而 Protobuf 呢?序列化 45ms,反序列化 52ms。差了将近 8 倍。
Protobuf:实时流的主力选手
Protobuf 是 Google 搞的,现在基本成了实时计算的事实标准。它的核心思路是:先定义好 Schema(.proto 文件),然后生成代码,序列化时只传字段编号和值,不传字段名。
举个例子:
// 定义 proto 文件
message Order {
int64 order_id = 1;
string user_name = 2;
double amount = 3;
int64 timestamp = 4;
}
// 序列化后只有 28 个字节
// 而同样的 JSON 要 78 个字节
你想想看,同样的数据,体积小了 60% 以上。网络传输时间自然就降下来了。
我个人习惯在 Kafka 和 Flink 场景里首选 Protobuf。原因很简单:
- 序列化/反序列化速度极快
- 生成的字节流很小
- 支持向后兼容的字段添加
Avro:Schema 演进的王者
Avro 和 Protobuf 很像,但有一个关键区别:Avro 的 Schema 是跟着数据一起走的。什么意思呢?就是每条数据前面都带着 Schema 信息,或者通过文件头/连接协商来传递。
这个特性在数据湖场景里特别有用。比如你用 Kafka 存数据,今天加了个字段,明天又删了一个。用 Protobuf 你得保证生产者和消费者同时更新代码。但用 Avro,配合 Schema Registry,可以做到无缝演进。
我记得在做一个实时数仓项目时,业务方每周都要加两三个字段。用 Protobuf 的话,每次都要发版、重启。换成 Avro + Schema Registry 后,加字段只需要在 Registry 里注册新版本,下游自动兼容。嗯,运维成本降了一大截。
性能方面,Avro 和 Protobuf 基本在同一量级。Avro 略慢一点点,但差距在 5% 以内,完全可以接受。
FlatBuffers:零反序列化的黑科技
FlatBuffers 是 Google 的另一个作品,思路和前面三个完全不同。它不搞「序列化 → 传输 → 反序列化」这套流程,而是直接把数据按二进制格式排好,你拿到字节流后可以直接读取字段,不需要反序列化。
说白了,就是「零拷贝」读取。
这个特性在延迟极度敏感的场景里是杀手级的。比如高频交易、游戏服务器、实时广告竞价。我见过一个广告系统,用 JSON 时端到端延迟 50ms,换成 FlatBuffers 后降到 5ms 以下。
但 FlatBuffers 也有代价:
- 写入数据比 Protobuf 慢(因为要预留空间)
- 生成的字节流体积略大
- 不支持动态 Schema 变更
性能对比一览表
下面这张表,是我基于实际项目数据整理的。测试环境:单条数据 10 个字段,10 万条循环,Java 环境。
| 指标 | JSON | Protobuf | Avro | FlatBuffers |
|---|---|---|---|---|
| 序列化耗时 (ms) | 320 | 45 | 52 | 78 |
| 反序列化耗时 (ms) | 410 | 52 | 60 | 0(无需反序列化) |
| 字节大小 (bytes) | 78 | 28 | 30 | 36 |
| Schema 演进 | 弱 | 强 | 极强 | 弱 |
| 人类可读 | 是 | 否 | 否 | 否 |
| 适用场景 | 调试/低吞吐 | 实时流/微服务 | 数据湖/Schema 多变 | 超低延迟/高频读 |
选型建议
说了这么多,到底怎么选?我个人的经验是:
- 默认选 Protobuf——性能、体积、易用性最均衡,适合 90% 的实时流场景
- Schema 变化频繁选 Avro——配合 Schema Registry,省去大量发版工作
- 延迟要求 < 1ms 选 FlatBuffers——零反序列化,但要做好写入性能下降的准备
- JSON 只用于调试——或者吞吐量极低(< 100 TPS)的内部工具
好了,序列化这块就聊到这儿。记住一句话:选对序列化方案,延迟优化就成功了一半。
公众号:蓝海资料掘金营,微信deep3321