知识卡片

Avro不靠标签号靠Reader/Writer模式匹配实现兼容且天然适配动态生成模式

结构图卡

内容

和[[Thrift与Protobuf靠不可变的字段标签号实现模式演变]]完全不同,Avro的模式里根本 没有标签号,编码数据只是把各字段的值紧紧拼接在一起,没有任何东西标记某段字节是 哪个字段或什么类型——因此Avro能做到本章所有编码里最紧凑(示例记录只需32字节), 但代价是解析时必须严格按模式声明的字段顺序遍历,且读数据的代码必须知道写数据时 用的确切模式,否则会解析错乱。Avro的解决方案是明确区分Writer模式(编码时用的模式) 和Reader模式(解码时期望的模式)——这两者不需要相同,只需要”兼容”:解码时Avro库 把两个模式并排比对,按字段名(而非位置或标签号)做匹配,Writer模式里有但Reader 模式没有的字段被忽略,Reader模式需要但Writer模式没提供的字段用Reader模式里声明的 默认值填充。这套机制下,兼容性规则简化成一条:只能新增或删除”带默认值”的字段—— 新增带默认值字段,旧数据(没这个字段的Writer模式)被新Reader读时会自动填充默认值, 保持向后兼容;类似地处理删除。Avro相比标签号方案的关键优势在于对动态生成的模式 更友好:比如要把关系数据库整表转储成Avro文件,可以直接从数据库表结构自动生成 Avro模式(列名映射成字段名),数据库改了结构(加列删列)就重新生成一份新模式再 导出,全程不需要人工维护”列名到标签号”的映射——而Thrift/Protobuf这类方案下,这个 映射通常需要人工分配和维护,动态生成模式并不是它们的设计目标。

结构图

flowchart LR
    A[Avro编码: 无标签号, 字段值紧密拼接] --> B[必须按模式声明顺序遍历解析]
    C[Writer模式: 编码时使用] -.不必与Reader模式相同.-> D[Reader模式: 解码时期望]
    C --> E[Avro库按字段名并排比对两个模式]
    E --> F[Writer有Reader无: 忽略该字段]
    E --> G[Reader需要Writer无: 用Reader声明的默认值填充]
    H[动态生成模式场景: 数据库表转储] -.列名直接映射字段名, 无需人工维护标签映射.-> H

参考来源

- 位置:《数据密集型应用系统设计》第四章《编码与演化》"Avro""Writer模式与Reader 模式""动态生成的模式"(源文件:_epub-src/ch4_split_001.html, ch4_split_002.html) - 结论依据:原文说明Avro编码没有标签号、只能靠严格遍历模式解析,Writer模式与 Reader模式不必相同、只需兼容,解码时按字段名匹配并用默认值填充缺失字段,并 举出从关系数据库表自动生成Avro模式的例子说明其对动态生成模式更友好,直接支撑 本卡片的结构梳理。 - 原始内容:首先,请注意模式中没有标签号码……Avro的关键思想是Writer模式和Reader 模式不必是相同的-他们只需要兼容……模式解析通过字段名匹配字段……不同之处在于 Avro对动态生成的模式更友好……数据库中的列名称映射到Avro中的字段名称。