DDIA-数据编码与演化

345 阅读3分钟

数据编码格式

本章介绍多种不同的编码数据的格式,包括:JSON、XML、Protocol Buffers、Thrift 和 Avro。关注它们如何处理模式变化(理解成表增加/删除字段)以及如何在系统中同时支持新、旧数据。

当我们需要将数据写入文本、或通过网络进行发送时必须将数据从内存中的表示(哈希表、树、对象)序列化成字符序列(例如JSON)。

JSON、XML 和 CSV

JSON、XML和CSV都是基于文本格式的编码方式,具有良好的可读性,但也存在一些弊端:

  1. 数据编码存在模糊之处。CSV和XML中无法区分【数字】和【恰好由数字组成的字符串】,JSON区分数字和字符串,但不区分整数和浮点数。
  2. XML和JSON不支持二进制字符串,数据不够紧凑空间占用大。
  3. CSV 没有任何模式,因此需要在应用程序中硬编码定义每行每列的含义。同时 CSV还是一个很模糊的格式,如果一个值包含逗号或换行符,会无法区分。

二进制编码

Thrift与Protocol Buffers

它们通过接口定义语言(IDL)来描述模式,编码任意数据。比如 Thrift 通过接口定义语言这样描述数据:

struct Person {
  1: required string				userName,
  2: optional i64						favoriteNumber,
  3: optional list<string>	interests
}

Protocol Buffers 等价模式定义:

message Person {
  required	string 	user_name					= 1;
  optional	int64		favorite_number		= 2;
  repeated	string	interests					= 3;
}

Thrift 和 Protocol Buffers 各有对应的代码生成工具,结合IDL生成支持多种编程语言的类。应用程序可以直接调用生成的代码来序列化和反序列化该模式的数据。

基于这个模式编码产生的数据都是二进制,它们与JSON的最大区别是编码后的内容没有字段名(userName, interests...)相反编码数据包含数字类型的字段标签(1、2、3)这些是 IDL 中出现的数字,相当于字段的别名但更加紧凑。

向前(旧代码读取新代码产生的数据)和向后(新代码读取旧代码产生的数据)兼容性:

  • 向前兼容性:可以在已存在的模式中添加字段,只要给新字段一个新的标记号码4。如果旧代码读取到新数据,包含一个它不能识别的字段4,忽略该字段4即可。实现上可以通过新字段4的数据类型注释,通知解析器跳过指定字节数。

  • 向后兼容性:只要保证新添加的字段都有一个唯一标记号码。这样即使是新代码,相同号码的字段仍是相同的含义。唯一的细节是:不能添加要求必输的新字段。这是因为旧代码产生的数据,该新字段必然是空值,新代码读到这种数据将检查失败。所以为保持向后兼容性,在模式初始化部署后,就只能添加可选字段或带有默认值的字段。

    带有默认值算是必输字段吗?