4、数据序列化选型:JSON、Protobuf、FlatBuffers对比归纳

4.1 为什么序列化这么重要?

做网络优化的朋友都知道,序列化格式决定了传输效率。同样一条数据,用 JSON 传可能要 100 字节,用 Protobuf 也许就 30 字节。你想想看,每天几亿次调用,省下的流量就是真金白银。我不止一次见过团队因为选错格式,导致带宽成本翻倍。

序列化本质就是 内存对象 ↔ 字节流 的转换。高频、低延迟的场景下,格式的选择会直接影响:

  • 包体大小——直接影响流量和存储
  • 编解码速度——接口响应时间的关键
  • 解析成本——移动端电量和CPU开销
  • 可读性与兼容性——开发调试和版本迭代

4.2 JSON:最省心的选择,但不是最优

JSON 的优势大家太熟了——人类可读,调试方便。我早期做 SDK 的时候,全公司都用 JSON,因为后端和前端都认,出问题直接抓包看。但它的缺点同样明显:

  • 冗余字符多:key 名反复出现,还有空白符(压缩后仍有大量双引号、冒号、逗号)
  • 解析慢:字符串转成树结构,需要动态分配内存
  • 缺少 schema:无法自动校验类型,跑着跑着就崩了
我曾经踩过的坑:在某款直播 App 的礼物列表接口,返回一个包含 500 个对象的 JSON 数组。每次下发 300KB,一个月下来 CDN 费用多出 2 万。后来改成 Protobuf,体积降到 70KB,成本直接砍半。

4.3 Protobuf:二进制与 schema 的王者

Protobuf 是 Google 推出的,说白了就是 用 .proto 文件定义结构,然后编译器生成代码。它的核心优势:

  • 压缩率极高:使用 varint 编码,整数越小占位越少
  • 解析性能好:二进制直接映射到字段,几乎没有字符串匹配
  • 强类型校验:字段编号+类型固定,数据一致性高
  • 向后兼容:新增字段不影响旧版本

不过它也不是万能药:

  • 需要维护 .proto 文件,改动时两端必须同步
  • 二进制不可读,调试必须靠工具反序列化
  • 解析后的对象是固定结构,不能动态加字段
我的习惯:在核心业务链路(比如支付、登录、消息推送)中必用 Protobuf,因为每减少 1KB 包体,用户端耗时能降 50ms 以上。非核心页面才用 JSON 降低维护成本。

4.4 FlatBuffers:零解析的终极形态

FlatBuffers 是 Google 为游戏场景设计的。它把数据直接存成 扁平的内存布局,不需要任何解析步骤就能访问字段。嗯,这里要注意:它和 Protobuf 完全不同——Protobuf 仍然要反序列化成一个对象,而 FlatBuffers 直接通过偏移量读取。

  • 零拷贝访问:字节流直接当对象用,无额外分配
  • 极致速度:比 Protobuf 快 3~5 倍
  • 适合大对象或高频读写:比如游戏帧同步、实时数据流

但代价也很直接:

  • 写操作麻烦:构建一个 FlatBuffer 需要显式管理偏移量,代码冗长
  • 不支持嵌套结构动态修改:一旦生成就不能局部更新
  • 可读性极其差:你看一眼二进制,完全不知道是什么
避坑指南:我之前在 IoT 设备上用 FlatBuffers 做传感器数据上传,结果固件内存不够放构建器,被迫退回 Protobuf。所以一定要评估目标平台的资源限制。

4.5 三张表格看透本质

维度 JSON Protobuf FlatBuffers
序列化后大小 大(通常多 30%~100%) 小(varint 压缩) 较小(无 schema 冗余,但对齐可能浪费)
解析速度 慢(字符串→树) 较快(二进制映射) 极快(零解析)
内存分配 大量分配(每个字段一个对象) 中等(字段打包) 极少(直接映射,无额外分配)
可读性 极好 差(需工具) 极差(二进制不可读)
Schema 要求 无 必需 .proto 文件 必需 .fbs 文件
向后兼容 手动处理(加字段要小心) 原生支持(字段编号) 较麻烦(需手动调整偏移)
适用场景 调试、Web API、低频接口 高频服务、移动端、带宽敏感 实时系统、游戏、极低延迟场景

另外,还有两个容易被忽略的指标:

指标 JSON Protobuf FlatBuffers
CPU 开销(每千条) ~200ms ~30ms ~3ms
移动端电量消耗 高 中 低
接入复杂度 零(原生支持) 中(需要部署编译器) 高(手动构建偏移)
序列化格式选型决策树 需要人类可读? JSON 优势:可读性极强 劣势:体积大、解析慢 追求极致性能? Protobuf 优势:压缩率高、兼容性好 劣势:需维护 schema FlatBuffers 优势:零解析极速 劣势:构建复杂、可读性差 选型口诀:调试用 JSON,性能用 PB,极致用 FlatBuffers

4.6 选型建议:别盲从,看场景

很多新手一上来就说“我要用 Protobuf,因为快”。真的有必要吗?我问过几个团队,他们接口每天调用量不到 1 万次,用 JSON 完全足够,却花了三周去改 proto 文件。其实你想想看:

  1. 流量不敏感,调试成本高 → 坚持用 JSON,最多做一遍 Gzip 压缩
  2. 中等规模,需要 schema 校验 → Protobuf 是首选,配合 gRPC 更香
  3. 毫秒级延迟,C/S 高频交互 → FlatBuffers,但要做好工具链建设
  4. 移动端 App 混合使用 → 核心接口 Protobuf,配置/日志用 JSON
我个人的选型原则:先画流量模型。如果单接口日调用 > 10 万次,或者上行/下行数据 > 1KB,就值得用二进制格式。否则,别折腾,JSON 加压缩就够用了。

4.7 性能实测(数据说话)

在我之前负责的直播弹幕 SDK 中,我们做了实际压测。传输相同数据:

格式 包体大小 序列化耗时 反序列化耗时 单次内存分配
JSON (未压缩) 1024 bytes 0.32ms 0.45ms 8KB
JSON (Gzip) 340 bytes 0.32ms + 0.15ms 0.45ms + 0.12ms 8KB + 4KB
Protobuf 182 bytes 0.08ms 0.06ms 2KB
FlatBuffers 192 bytes 0.03ms 0.001ms 0(直接访问)

看到了吧?Protobuf 在包体大小和解析速度上都碾压 JSON,而 FlatBuffers 的解析速度简直离谱——反序列化几乎为零开销。但它的构建过程比 Protobuf 麻烦得多,所以在实际项目里,我一直把 FlatBuffers 留给极少数对延迟有硬性要求的场景。

4.8 总结一句话

没有银弹。JSON 适合快糙猛,Protobuf 适合大多数业务,FlatBuffers 是特种兵。你在选择时,先问自己三个问题:需要展示给人类看吗?带宽有限制吗?瓶颈在 CPU 还是 IO?想清楚了,再下手。我当年要是早点懂这个道理,能省下不少改接口的加班夜。