进入到第四章了,本篇主要聊的点是编码(也就是序列化)与代码升级的一些场景,来梳理存储之中涉及到的编解码的流程。目前主流的编解码便是来自Apache的Avro,来自Facebook的Thrift与Google的Protocolbuf,在本篇之中,我们也会一一梳理各种编码的优点与痛点。

1.非二进制的编码格式

程序通常以至少两种不同的表示方式处理数据:

1、在内存中,数据是保存在对象、结构、列表、数组、哈希表、树、等等。这些数据结构在内存之中被优化为CPU可以高效访问和操作的结构(通常这是操作系统的任务,并不需要程序员操心)。

2、而当你想把数据写入一个文件或者通过网络发送它时,你必须把它编码成某种形式的字节序列(例如,一个JSON文档)。

因此,我们需要两种形式之间的某种转换。(内存与其他位置)翻译从内存中表示的数据称之为编码(也称为序列化),反之称为解码(反序列化)。

通常编码有如下几种格式:

  • 特定的语言格式

    许多编程语言都对编码有内置的支持,用于将内存对象编码成字节序列。例如:Java的java.io.Serializable , Ruby的Marshal, Python的pickle。但是这些编程语言内置的库存在一些深层次的问题。
  • 编码通常与特定的编程语言捆绑在一起,用另一种语言读取数据是非常困难的
  • 为了在同一对象类型中恢复数据,解码过程需要能够实例化任意类,如果攻击者可以让您的应用程序解码任意字节序列,则它们可以实例化任意类。这常常是安全问题的来源。
  • 效率(用于编码或解码的CPU时间,以及编码结构的大小),java内置编码库臭名昭著的就是其糟糕的表现和臃肿的编码

  • JSON、XML与CSV

    上面这几种格式,也是我们在编码之中常见到的。
  • XML的描述十分精准,但是因过于冗长。
  • JSON的流行主要归功于它在Web浏览器中的内置支持(由于它是JavaScript的一个子集)和相对于XML的简单性。
  • CSV是另一种流行的与语言无关的格式,尽管功能不强。

JSON、XML和CSV都是文本格式,因此都具有一定的可读性。但他们也有如下一些微妙的问题:

  • 关于数字的编码有很多歧义。在XML和CSV中,不能区分恰好由数字组成的数字和字符串(除了引用外部模式)。JSON区分字符串和数字,但它不区分整数和浮点数,也不能确认精度。
  • JSON与XML为Unicode字符串的支持,但他们不支持二进制字符串(字节序列没有字符编码)。
  • 对于XML和JSON,都有可选的模式支持。这些模式语言非常强大,因此学习和实现起来相当复杂。而CSV没有任何模式,因此需要应用程序定义每个行和列的含义。如果应用程序添加了新行或列,则必须手动处理该更新。CSV是一个相当模糊的格式(出于是分隔符的原因)

2.二进制的编码格式

二进制的编码格式通常是最紧凑的编码格式,对于一个小的数据集,编码大小的收益是微不足道的,但一旦进入百万兆字节的数据集,数据格式的选择就会有很大的影响了。接下来我们来看一个通过JSON描述的数据结构:

  • MessagPack

    我们来看看通过MessagePack进行二进制编码之后的JSON格式:



    二进制编码长度为66个字节,这仅比81字节的文本JSON编码小了一点。通过这样的空间减少便丧失了可读性的保障,我们来看看有木有更优秀的解决方式。
  • Thrift

    在Thrift中的数据进行编码,需要预先在Thrift接口定义语言(IDL)中描述这样的模式:



    在Thrift之中存在两种不同的二进制编码格式,一种是直接使用二进制编码的Binary格式,另一种则是使用压缩之后的Compact格式,我们来一一看两者的区别。



Binary格式编码之后为59个字节大小,并且每个字段都有一个类型注释(用于指示它是字符串、整数、列表等),并在需要时指定长度指示(字符串的长度、列表中项的数量)。但是和MessagePack相比就省去了字段名等信息,取而代之的是字段标记(1,2和3),这些是出现在模式定义中的数字。字段标记类似于字段别名,它们是一种简洁的方式来描述我们所谈论的字段,而不必拼写字段名称。从而减少了二进制编码的大小。



Compact格式它包含相同的信息只有34个字节。它通过将字段类型和标记号打包成一个字节,并使用可变长度整数来实现这一点。它不是为1337号使用八个完整的字节,而是用两个字节编码,每个字节的最高位用来指示是否还有更多的字节要来。这意味着64到63之间的数字用一个字节编码,8192到8191之间的数字用两个字节编码,较大的数字使用更多字节。

  • ProtocolBuf

    Protocolbuf(只有一个二进制编码格式)相同的数据编码如下图所示。它位包装略有不同,但Thrift的Compact格式大同小异。Protobuf以33字节匹配相同的记录。

  • Avro

    Avro是一个二进制编码格式,它是发源于开源项目Hadoop,来作为Thrift的替换方案存在的,我们来看看通过Avro编码之后的记录,又是怎么样的呢?



    在Avro模式之中没有标记号。将同样的数据进行编码,Avro二进制编码是32个字节长,是上述编码之中最紧凑的。检查上述的字节序列,并没有标识字段或数据类型。编码简单地由连接在一起的值组成。在解析二进制数据时,通过使用模式来确定每个字段的数据类型。这意味着如果读取数据的代码与写入数据的代码使用完全相同的模式,二进制数据才能被正确地解码。

3.模式升级与演化

随着应用程序的开发,模式不可避免地需要随着时间而改变。而在这个过程之中,二进制编码同时保持向后和向前兼容性呢?

  • 字段标记
  • 从示例中可以看到,编码的记录只是编码字段的串联。每个字段由标签号码和注释的数据类型识别(如字符串或整数)。如果没有设置字段值,则只需从已编码的记录中省略该字段值。因此字段标记对编码数据的含义至关重要。我们可以更改模式中字段的名称,因为编码的数据从不引用字段名称,但不能更改字段的标记,因为这将使所有现有编码数据无效。
  • 可以通过添加一个新的标记号的方式向模式添加新字段。如果旧代码(不知道您添加的新标记号)试图读取由新代码编写的数据,包括一个新字段,该字段的标记号不识别,它可以简单地忽略该字段。数据类型注释允许分析器来确定需要跳过多少字节。因为每个字段都有唯一的标记号,新代码可以无缝连接旧的数据,因为标记号仍然具有相同的含义。但是,如果是添加了一个新字段,则不能使它成为必需字段。如果要添加一个字段并使其成为必需的字段,那么如果新代码读取旧代码编写的数据,则该检查将失败,因为旧代码将不会写入您添加的新字段。因此,为了保持向后兼容性,在初始部署模式之后添加的每个字段必须是可选的或具有默认值。
  • 删除字段就像添加字段一样,这意味着只能删除一个可选的字段(必填字段不能被删除),而且您不能再次使用相同的标记号(因为您可能还有一个包含旧标记号的数据,该字段必须被新代码忽略)。

  • 数据类型

    如何改变字段的数据类型?例如,将32位整数转换为64位整数。新代码可以很容易地读取旧代码编写的数据,因为解析器可以用零填充任何丢失的位。但是,如果旧代码读取由新代码编写的数据,旧代码仍然使用32位变量来保存值。如果解码的64位值不适合32位,会被截断。

    Protocolbuf并没有一个列表或数组的数据类型,而是有一个重复的标记字段。可以将可选的(单值)字段转换为重复的(多值)字段。读取旧数据的新代码看到一个具有零个或一个元素的列表(取决于字段是否存在);读取新数据的旧代码只看到列表的最后一个元素。而Thrift有一个专门的列表数据类型,这是参数列表中的数据类型。这不允许像Protocolbuf那样从单值到多值的升级,但它具有支持嵌套列表的优点。

  • 动态生成模式

    Avro最大的特点是支持了动态生成模式,它的核心思想是编码者与解码者的模式可以不同,事实上他们只需要兼容就可以了。相比于Protocolbuf和Thrift,它并不包含任何标签数字。每当数据库模式发生变化时,管理员必须手动更新从数据库列名到字段标记的映射。而Avro是每次运行时简单地进行模式转换。任何读取新数据文件的程序都会感知到记录的字段发生了变化。

4.小结

编码的细节不仅影响到工作效率,更重要的是会影响到应用程序和软件的架构。Prorotocol Buf,Thrift 与 Avro,都使用一个模式来描述一个二进制编码格式。它们的模式语言比XML模式或JSON模式要简单得多,它支持更详细的验证规则,并且能够更好的进行模式的演化升级,在性能上也有了更好的提升。

编码与模式------《Designing Data-Intensive Applications》读书笔记5的更多相关文章

  1. 《企业应用架构模式》(POEAA)读书笔记

    原文地址:<企业应用架构模式>(POEAA)读书笔记作者:邹齐龙(技术-5013 什么是架构 Rolph Johnson认为:架构是一种主观上的东西,是专家级的项目开发人员对系统设计的一些 ...

  2. <代码整洁之道>、<java与模式>、<head first设计模式>读书笔记集合

    一.前言                                                                                       几个月前的看书笔记 ...

  3. python for data analysis 2nd 读书笔记(一)

    第一章相对简单,也么有什么需要记录的内容,主要用到的工具的简介及环境配置,粗略的过一下就行了.下面我们开始第二章的学习 CHAPTER 22.2Python Language Basics, IPyt ...

  4. HeadFirst设计模式读书笔记--目录

    HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern) HeadFirst设计模式读书笔记(2)-观察者模式(Observer Pattern) HeadFirst设计 ...

  5. 可靠的、可扩展的、可维护的数据系统 ------《Designing Data-Intensive Applications》读书笔记1

    坦白说也是机缘巧合,在硕士生阶段进入分布式系统领域学习.无论是大规模存储或计算,其核心也是运用分布式技术利用并行性来解决数据密集型应用的需求.最近开始在啃这本<Designing Data-In ...

  6. 数据模型与查询语言 ------《Designing Data-Intensive Applications》读书笔记2

    数据模型是开发软件的最重要的部分,因为它们对应用程序有着深远的影响:不仅是软件的编写方式,而且也影响我们如何解决的问题的方式.第二篇读书笔记,我们聊一聊数据模型的设计. 1.数据模型的分层 作为一个开 ...

  7. OLAP与数据仓库------《Designing Data-Intensive Applications》读书笔记4

    由于第三章的内容比较多,这里我们拆分成两篇读书笔记来记录.上一章我们聊了聊如何数据库是如何实现存储和检索的,今天这篇我们继续来看看OLTP与OLAP存储引擎的区别与联系. 1.OLTP与OLAP 联机 ...

  8. 副本机制与副本同步------《Designing Data-Intensive Applications》读书笔记6

    进入到第五章了,来到了分布式系统之中最核心与复杂的内容:副本与一致性.通常分布式系统会通过网络连接的多台机器上保存相同数据的副本,所以在本篇之中,我们来展开看看如何去管理和维护这些副本,以及这个过程之 ...

  9. 读书笔记《PHP高级程序设计、模式、框架与测试》

    序言 闲来无事,下载了一些电子书,然后看书名不错<PHP高级程序设计_模式.框架与测试>,翻了一下虽然书有点老了但是讲的内容经常会碰到!给大家推荐一下,然后这里放上我的读书笔记,每日更新. ...

  10. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16

    一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...

随机推荐

  1. SSH Secure Shell显示serverTomcat后台内容

    作为linux小白,仅仅有学一点记一点了: 部署server的时候.常常须要向本地一样查看控制台输出,在linux上能够通过查看日志输出替代,当然也能够通过命令让日志实时显示在命令窗体,这对用惯了wi ...

  2. 基于 HTML5 Canvas 的 3D 机房创建

    对于 3D 机房来说,监控已经不是什么难事,不同的人有不同的做法,今天试着用 HT 写了一个基于 HTML5 的机房,发现果然 HT 简单好用.本例是将灯光.雾化以及 eye 的最大最小距离等等功能在 ...

  3. timestamp时间戳的应用(微信小程序开发也一样)

    在微信小程序开发时发现一个timestamp的时间戳的变量 比如获取微信运动步数时候 timestamp是如何形成的在JS中 是这么形成的 var timestamp = Date.parse(new ...

  4. 【Sqlserver系列】初级思维导图

    1   概述 本篇文章主要概述Sqlserver思维导图. 2   具体内容 3   参考文献 [01]https://mp.weixin.qq.com/s/USNMslpvu7pWosMZnVTPd ...

  5. make工程管理器

    1.概述 大型程序中,人们希望工具自动识别修改的文件,而且不需要输入冗长的命令,就可以进行编译链接等操作,于是make工程管理器应运而生. make可以自动识别文件时间戳,只处理修改的文件: make ...

  6. 关于《Head First Python》一书中print_lol()函数的思考

    关于<Head First Python>一书中print_lol()函数的思考 在<Head First Python>第一章中,讲述到Python处理复杂数据(以电影数据列 ...

  7. springboot swagger-ui结合

    随着移动互联的发展,前后端的分离已经是趋势.前后端已不是传统部门的划分,而是它们各有一套的生态系统,包括不同的开发语言.不同的开发流程.构建方式.测试流程等.做前端的不需要会maven作为构建工具,后 ...

  8. 排查程序死循环,死锁的方法 ——pstack

    pstack命令可显示每个进程的栈跟踪,pstack $pid即可,pstack命令须由$pid进程的属主或者root运行. 这次出现cpu占比100%的情况,但看memory占比,并无异常,怀疑是某 ...

  9. Spring事务管理总结

    本文是对慕课网上"搞定SSM开发"路径的系列课程的总结,详细的项目文档和课程总结放在github上了.点击查看 本文对应慕课网上课程Spring事务管理,详情可查看:点我 1: 概 ...

  10. bzoj 3575: [Hnoi2014]道路堵塞

    Description A 国有N座城市,依次标为1到N.同时,在这N座城市间有M条单向道路,每条道路的长度是一个正整数.现在,A国交通部指定了一条从城市1到城市N的路径, 并且保证这条路径的长度是所 ...