3、数据格式与序列化:JSON、CSV、Protobuf、Avro、Parquet 的对比与选择

做数据录制回放,说白了就是在跟「格式」打交道。

你录下来的数据,用什么装?怎么存?怎么读?这直接决定了你的系统能跑多快、能撑多大。我见过不少团队,前期图省事选了 JSON,结果到了回放阶段,性能瓶颈卡得死死的,最后不得不重构。嗯,今天我们就来把这几种主流格式掰开揉碎讲清楚。

3.1 为什么数据格式这么重要?

数据格式决定了三件事:

  • 体积:同样的数据,不同格式能差出 10 倍以上
  • 速度:序列化/反序列化的开销,直接影响录制和回放的吞吐
  • 兼容性:字段增删、类型变化,能不能平滑演进

我个人习惯,选格式之前先问自己三个问题:

  1. 数据是给机器读,还是给人看?
  2. 数据量级是 KB 级,还是 TB 级?
  3. Schema 会不会频繁变化?

答案不同,选择天差地别。

3.2 五种格式速览

格式 类型 可读性 压缩率 序列化速度 Schema 支持 典型场景
JSON 文本 ★★★★★ ★☆☆☆☆ ★★☆☆☆ 弱(无强制) API 调试、小规模数据
CSV 文本 ★★★★☆ ★★☆☆☆ ★★★☆☆ 无 日志导出、Excel 交换
Protobuf 二进制 ★☆☆☆☆ ★★★★☆ ★★★★★ 强(.proto 文件) 微服务 RPC、高性能录制
Avro 二进制 ★★☆☆☆ ★★★★☆ ★★★★☆ 强(内嵌 Schema) Kafka 消息、数据管道
Parquet 列式二进制 ★☆☆☆☆ ★★★★★ ★★★☆☆ 强(内嵌 Schema) 大数据分析、数仓存储

核心结论:没有银弹。选格式就是做 trade-off。

3.3 JSON:最灵活,也最「贵」

JSON 的好处不用多说——人类可读,任何语言都能解析。我刚开始做录制系统时,第一版用的就是 JSON。调试起来确实方便,curl 一把梭。

但问题也很明显:

  • 体积大:每个字段名都要重复出现,一个简单的 {"user_id": 123, "action": "click"} 就要 30 多个字节
  • 解析慢:字符串转数字、转布尔,每一步都有开销
  • 无 Schema 约束:字段名拼错了也不会报错,回放时才发现数据对不上

避坑指南:我曾经在一个高吞吐录制项目里用 JSON,结果单机每秒只能处理 2 万条。换成 Protobuf 后,直接飙到 15 万条。嗯,血的教训。

3.4 CSV:简单粗暴,但坑不少

CSV 适合什么?日志导出、给业务同学看的数据快照。你想想看,用 Excel 打开 CSV 直接就能分析,多方便。

但做录制回放时,CSV 有几个致命伤:

  • 没有类型信息:所有字段都是字符串,回放时得自己转类型
  • 嵌套结构不支持:一个字段里如果有数组或对象,CSV 就抓瞎了
  • 转义规则混乱:字段里带逗号或换行符,解析器可能直接崩掉

我个人建议:CSV 只用于「一次性导出」或「人工检查」,别用在核心录制链路上。

3.5 Protobuf:高性能录制的首选

Protobuf 是 Google 搞的,说白了就是「定义好数据结构,然后生成代码,序列化时直接写二进制」。

先看一个简单的 .proto 定义:

syntax = "proto3";

message RecordEvent {
  int64 timestamp = 1;
  string user_id = 2;
  string action = 3;
  map<string, string> properties = 4;
}

编译后,序列化出来的二进制体积只有 JSON 的 1/3 到 1/5。而且解析速度极快——不需要字符串解析,直接按偏移量读内存。

我为什么推荐它做录制?

  • 强类型:字段类型写死了,不会出现「这个字段到底是 int 还是 string」的尴尬
  • 向前向后兼容:加字段时用 optional,老代码读到新数据也不会崩
  • 跨语言:Java、Go、Python、C++ 都能生成对应的代码

小技巧:录制时可以用 Protobuf 的 FieldMask 特性,只录制需要的字段,进一步减少体积。

3.6 Avro:数据管道的瑞士军刀

Avro 和 Protobuf 很像,但有一个关键区别:Schema 和数据一起存。

什么意思?Avro 文件里,开头就写明了 Schema。读数据时,解析器自动知道每个字段叫什么、什么类型。这在数据管道里特别有用——你不需要提前约定好 .proto 文件,拿到文件就能读。

举个例子,Avro 的 Schema 定义:

{
  "type": "record",
  "name": "RecordEvent",
  "fields": [
    {"name": "timestamp", "type": "long"},
    {"name": "user_id", "type": "string"},
    {"name": "action", "type": "string"}
  ]
}

Avro 在 Kafka 生态里用得特别多。我记得有一次帮客户搭实时录制管道,数据从 Kafka 进来,直接序列化成 Avro,下游 Flink 消费时自动解析 Schema,完全不用手动维护版本。

但 Avro 也有缺点:

  • Schema 本身占空间,小数据量时反而比 Protobuf 大
  • 解析时多了一步 Schema 读取,速度略慢于 Protobuf

3.7 Parquet:大数据分析的王者

Parquet 是列式存储格式。你想想看,传统行式存储(JSON、CSV)读数据时,哪怕你只想要一个字段,也得把整行读进来。Parquet 不一样——它按列存,读哪列取哪列。

这对回放场景意味着什么?

  • 查询快:如果你只回放某个用户的记录,Parquet 可以只扫描相关列
  • 压缩率高:同一列的数据类型相同,压缩算法效果极好
  • 适合批量:单条写入慢,但批量写入和读取非常快

我一般在做「历史数据回放」时用 Parquet。比如录制了 1TB 的原始数据,需要按时间范围回放分析,Parquet 配合 Spark 或 Presto,查询速度能比 CSV 快 10 倍以上。

注意:Parquet 不适合实时录制。它的写入开销大,单条追加性能很差。实时录制用 Protobuf 或 Avro,归档分析时再转 Parquet。

3.8 如何选择?一张流程图帮你理清

下面这张图是我自己总结的选型逻辑,你可以照着走一遍:

数据格式选型 数据是否需要人工阅读? 是 JSON/CSV 否 实时录制还是批量分析? 实时 Schema 是否频繁变化? 是 Avro 否 Protobuf 批量 Parquet 选型逻辑:先看用途,再看场景,最后看 Schema 稳定性 推荐组合:实时录制用 Protobuf,归档分析转 Parquet 中间管道用 Avro,调试和导出用 JSON/CSV

3.9 实战建议:我的「三段式」策略

做了这么多年数据录制回放,我总结了一套比较稳的套路:

  1. 录制阶段:用 Protobuf。性能优先,Schema 提前定义好,生成代码后直接序列化写入文件或消息队列
  2. 传输阶段:用 Avro。Kafka 或 Pulsar 里走 Avro,Schema Registry 自动管理版本,上下游解耦
  3. 分析阶段:转 Parquet。用 Spark 或 Flink 批量转换,然后丢到数仓里做 OLAP 查询

小提示:如果你用的是云服务,比如 AWS 的 S3 + Athena,Parquet 几乎是必选。Athena 按扫描数据量计费,Parquet 能帮你省 60% 以上的费用。

3.10 总结

数据格式没有绝对的好坏,只有合不合适。

  • 要可读性,选 JSON/CSV
  • 要性能,选 Protobuf
  • 要管道兼容,选 Avro
  • 要分析效率,选 Parquet

你想想看,如果你的录制系统每秒要处理 10 万条事件,用 JSON 就是给自己挖坑。反过来,如果只是调试几个接口,上 Protobuf 反而有点杀鸡用牛刀。

嗯,选型这事,多踩几次坑就明白了。

可可精品资料库