4、Avro Schema 基础:Avro 数据类型、Schema 定义、与 JSON 的对比
好,我们进入 Schema 变更自动适配的第一块基石——Avro Schema。
你可能会问:为什么偏偏是 Avro?市面上序列化格式那么多,JSON、Protobuf、Thrift 哪个不能用?
我个人习惯把 Avro 叫做「为 Schema 演化而生的格式」。它从设计之初就把 Schema 和数据的绑定关系放在了第一位。说白了,没有 Schema,Avro 的数据就是一堆二进制乱码;有了 Schema,你才能读懂它、校验它、甚至自动适配它的变化。
4.1 Avro 支持的数据类型
Avro 的数据类型分两大类:基本类型和复杂类型。我刚开始接触时觉得它类型少,用久了才发现——够用,而且精炼。
4.1.1 基本类型
| 类型 | 说明 | JSON 示例 |
|---|---|---|
null |
空值,没有数据 | null |
boolean |
布尔值 | true / false |
int |
32 位有符号整数 | 42 |
long |
64 位有符号整数 | 1234567890123 |
float |
32 位单精度浮点数 | 3.14 |
double |
64 位双精度浮点数 | 3.1415926535 |
bytes |
字节序列 | "\u00FF" |
string |
UTF-8 编码字符串 | "hello" |
long,后来发现不同服务对毫秒/微秒的理解不一致,导致数据对不上。建议团队统一约定时间戳的精度,或者在 Schema 里用注释写清楚。
4.1.2 复杂类型
复杂类型才是 Avro 真正体现灵活性的地方。我挑几个重点说说。
- record:最常用的结构,类似 JSON 对象或数据库表行。包含多个命名字段,每个字段有自己的类型。
- enum:枚举类型,定义一组固定的符号。注意:枚举值一旦发布,不能删除,只能追加。
- array:数组,所有元素必须是同一类型。
- map:键值对,键必须是 string,值可以是任意类型。
- union:联合类型,表示字段可以是多种类型之一。这是 Schema 演化的关键——比如
["null", "string"]表示字段可以为 null 或字符串。 - fixed:固定长度的字节序列,适合存 UUID、哈希值等。
重点记住:union 类型是 Avro 实现向后兼容的「秘密武器」。你想想看,如果新版本给 record 加了一个字段,只要把它声明为 ["null", "新类型"],旧数据就能自动补 null,不会报错。
4.2 Schema 定义:JSON 格式的「数据契约」
Avro Schema 本身是用 JSON 写的。嗯,你没听错——描述数据的 Schema 也是 JSON。这样做的好处是:任何语言都能解析 Schema,不需要额外的解析器。
来看一个完整的例子。假设我们要描述一个用户信息的数据结构:
{
"type": "record",
"name": "User",
"namespace": "com.example.avro",
"doc": "用户基本信息",
"fields": [
{"name": "userId", "type": "long", "doc": "用户唯一标识"},
{"name": "username", "type": "string", "doc": "用户名"},
{"name": "email", "type": ["null", "string"], "default": null, "doc": "邮箱"},
{"name": "age", "type": "int", "default": 0, "doc": "年龄"},
{"name": "tags", "type": {"type": "array", "items": "string"}, "default": [], "doc": "标签列表"}
]
}
这里有几个关键点,我展开说说:
- type:最外层必须是
"record",表示这是一个复合结构。 - name:记录的名字,全限定名由
namespace + name组成。 - fields:字段列表,每个字段必须有
name和type。 - default:可选,指定字段的默认值。读旧数据时,如果新 Schema 多了字段,就用默认值填充。
- doc:文档注释,我建议每个字段都写上,团队协作时能省很多沟通成本。
default,而且类型又不是 union(比如 ["null", "string"]),那么读旧数据时如果缺少该字段,Avro 会直接抛异常。我曾经因为这个原因导致线上消费任务中断了半小时——教训就是:新增字段一定要给默认值,或者用 union 包一层 null。
4.3 Avro Schema vs JSON:为什么不用 JSON 直接干?
这个问题我经常被问到。JSON 多方便啊,人类可读,浏览器原生支持,为什么还要搞一套 Avro?
我画了一张对比图,你看完就明白了。
说白了,JSON 适合「人看数据」,Avro 适合「机器处理数据」。在数据同步场景下,我们追求的是:体积小、速度快、Schema 变化时不出错。这三点 Avro 都做得比 JSON 好。
4.4 实战建议:什么时候用 Avro?
根据我的经验,以下场景优先考虑 Avro:
- 大数据管道:Kafka、Spark、Flink 等场景,数据量巨大,Avro 的压缩率和性能优势明显。
- 跨团队数据交换:不同团队维护不同服务,Schema 变更需要自动适配,Avro + Schema Registry 是标配。
- 长期存储:数据要存几年,Schema 肯定会变。Avro 的演化能力让你不用重写历史数据。
一个小技巧:如果你刚开始用 Avro,建议先用 Avro Tools 把 JSON 数据转成 Avro 格式,体验一下序列化后的体积变化。我记得第一次看到 1GB 的 JSON 转成 Avro 只剩 200MB 时,确实被震撼到了。
好,这一章我们理清了 Avro 的数据类型、Schema 定义方式,以及它和 JSON 的本质区别。下一块内容,我们会深入 Schema 演化的具体规则——哪些变更安全,哪些会炸,以及如何用代码自动检测兼容性。