第四章 编码与演化
最后更新于:2022-04-02 04:18:30
[TOC]
## 概述
本章中将介绍几种编码数据的格式,包括 JSON,XML,Protocol Buffers,Thrift和Avro
在Web服务中,具象状态传输(REST)和远程过程调用(RPC),以及消息传递系统(如Actor和消息队列)
## 编码数据的格式
程序通常(至少)使用两种形式的数据:
1. 在内存中,数据保存在对象,结构体,列表,数组,哈希表,树等中。 这些数据结构针对CPU的高效访问和操作进行了优化(通常使用指针)。
2. 如果要将数据写入文件,或通过网络发送,则必须将其**编码(encode)**为某种自包含的字节序列(例如,JSON文档)。 由于每个进程都有自己独立的地址空间,一个进程中的指针对任何其他进程都没有意义,所以这个字节序列表示会与通常在内存中使用的数据结构完全不同
从内存中表示到字节序列的转换称为**编码(Encoding)**(也称为**序列化(serialization)**或**编组(marshalling)**),反过来称为**解码(Decoding)**[ii](https://vonng.gitbooks.io/ddia-cn/content/ch4.html#fn_ii)(**解析(Parsing)**,**反序列化(deserialization)**,**反编组( unmarshalling)**)
## 语言特定的格式
许多编程语言都内建了将内存对象编码为字节序列的支持。例如,Java有java.io.Serializable ,Ruby有Marshal,Python有pickle 等等
* 内置编程语言都与其语言深度绑定,不能被其他语言反序列化。
* 为了恢复相同对象类型的数据,解码过程需要**实例化任意类**的能力,这通常是安全问题的一个来源:如果攻击者可以让应用程序解码任意的字节序列,他们就能实例化任意的类,这会允许他们做可怕的事情,如远程执行任意代码。
* 在这些库中,数据版本控制通常是事后才考虑的。因为它们旨在快速简便地对数据进行编码,所以往往忽略了前向后向兼容性带来的麻烦问题。
* 效率(编码或解码所花费的CPU时间,以及编码结构的大小)往往也是事后才考虑的。 例如,Java的内置序列化由于其糟糕的性能和臃肿的编码而臭名昭着。
总结:因此,除非临时使用,采用语言内置编码通常是一个坏主意
## JSON,XML和二进制变体
1. 数字的编码多有歧义之处。XML和CSV不能区分数字和字符串(除非引用外部模式)。 JSON虽然区分字符串和数字,但不区分整数和浮点数,而且不能指定精度
2. 当处理大量数据时,这个问题更严重了。例如,大于$2^{53}$的整数不能在IEEE 754双精度浮点数中精确表示,Twitter API返回的JSON包含了两种推特ID,一个JSON数字,另一个是十进制字符串,以此避免JavaScript程序无法正确解析数字的问题
3. JSON 有与 XML 不支持二进制,需要使用base64,但它有点hacky,并增加了33%的数据大小
4. CSV没有任何模式,因此应用程序需要定义每行和每列的含义
## 二进制编码
对于仅在组织内部使用的数据,使用最小公分母编码格式的压力较小。例如,可以选择更紧凑或更快的解析格式。虽然对小数据集来说,收益可以忽略不计,但一旦达到TB级别,数据格式的选择就会产生巨大的影响
这一事实导致大量二进制编码版本JSON & XML的出现(MessagePack,BSON,BJSON,UBJSON,BISON和Smile等)(例如WBXML和Fast Infoset)。这些格式已经被各种各样的领域所采用,但是没有一个像JSON和XML的文本版本那样被广泛采用
';