进入到第四章了,本篇主要聊的点是编码(也就是序列化)与代码升级的一些场景,来梳理存储之中涉及到的编解码的流程。目前主流的编解码便是来自Apache的Avro,来自Facebook的Thrift与Google的Protocolbuf,在本篇之中,我们也会一一梳理各种编码的优点与痛点。
程序通常以至少两种不同的表示方式处理数据:
1、在内存中,数据是保存在对象、结构、列表、数组、哈希表、树、等等。这些数据结构在内存之中被优化为CPU可以高效访问和操作的结构(通常这是操作系统的任务,并不需要程序员操心)。
2、而当你想把数据写入一个文件或者通过网络发送它时,你必须把它编码成某种形式的字节序列(例如,一个JSON文档)。
因此,我们需要两种形式之间的某种转换。(内存与其他位置)翻译从内存中表示的数据称之为编码(也称为序列化),反之称为解码(反序列化)。
通常编码有如下几种格式:
JSON、XML和CSV都是文本格式,因此都具有一定的可读性。但他们也有如下一些微妙的问题:
二进制的编码格式通常是最紧凑的编码格式,对于一个小的数据集,编码大小的收益是微不足道的,但一旦进入百万兆字节的数据集,数据格式的选择就会有很大的影响了。接下来我们来看一个通过JSON描述的数据结构:
使用JSON描述的数据结构
通过MessagePack进行编码后的二进制格式 二进制编码长度为66个字节,这仅比81字节的文本JSON编码小了一点。通过这样的空间减少便丧失了可读性的保障,我们来看看有木有更优秀的解决方式。
通过IDL描述Thrift的数据格式 在Thrift之中存在两种不同的二进制编码格式,一种是直接使用二进制编码的Binary格式,另一种则是使用压缩之后的Compact格式,我们来一一看两者的区别。
Binary格式
Binary格式编码之后为59个字节大小,并且每个字段都有一个类型注释(用于指示它是字符串、整数、列表等),并在需要时指定长度指示(字符串的长度、列表中项的数量)。但是和MessagePack相比就省去了字段名等信息,取而代之的是字段标记(1,2和3),这些是出现在模式定义中的数字。字段标记类似于字段别名,它们是一种简洁的方式来描述我们所谈论的字段,而不必拼写字段名称。从而减少了二进制编码的大小。
Compact格式
Compact格式它包含相同的信息只有34个字节。它通过将字段类型和标记号打包成一个字节,并使用可变长度整数来实现这一点。它不是为1337号使用八个完整的字节,而是用两个字节编码,每个字节的最高位用来指示是否还有更多的字节要来。这意味着64到63之间的数字用一个字节编码,8192到8191之间的数字用两个字节编码,较大的数字使用更多字节。
ProtocolBuf的编码格式
Avro的编码格式 在Avro模式之中没有标记号。将同样的数据进行编码,Avro二进制编码是32个字节长,是上述编码之中最紧凑的。检查上述的字节序列,并没有标识字段或数据类型。编码简单地由连接在一起的值组成。在解析二进制数据时,通过使用模式来确定每个字段的数据类型。这意味着如果读取数据的代码与写入数据的代码使用完全相同的模式,二进制数据才能被正确地解码。
随着应用程序的开发,模式不可避免地需要随着时间而改变。而在这个过程之中,二进制编码同时保持向后和向前兼容性呢?
编码的细节不仅影响到工作效率,更重要的是会影响到应用程序和软件的架构。Prorotocol Buf,Thrift 与 Avro,都使用一个模式来描述一个二进制编码格式。它们的模式语言比XML模式或JSON模式要简单得多,它支持更详细的验证规则,并且能够更好的进行模式的演化升级,在性能上也有了更好的提升。