第四章 数据编码与演化

种地  金牌会员 | 2024-11-10 22:10:14 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 923|帖子 923|积分 2769

应用步伐总是增增改改,而修改步伐大多数情况下也在修改存储的数据


  • 数据格式发生改变时,必要代码更改:
    服务端:rolling update/ staged rollout,即灰度发布
    客户端:用户可能相称长一段时间都不会升级软件
  • 存在题目:新旧版本的代码,以及新旧版本数据格式在体系中同时共存。为了体系正常运行,必要保持双向兼容性:
    向后兼容:新代码可以读旧数据--可通过保留旧代码即可读取旧数据
    向前兼容:旧代码可以读新数据--比较棘手,旧版步伐必要忽略新版数据格式中新增的部分
  • 解决方案:通过几种编码数据的格式,对新旧代码数据必要共存的体系提供支持
编码数据的格式

步伐中至少使用两种情势的数据

  • 在内存中,数据生存在对象,结构体,列表,数组,哈希表,树中。这些数据结构针对CPU的高校访问和操纵举行了优化(通常使用指针)
  • 如果要将数据写入文件,或通过网络发送,则必须将其encoding为某种自包含的字节序列(如,JSON文档),由于每个进程都有自己独立的所在空间,一个进程中的只针对任何其他进程都没故意义,所以这个字节序列表示会与通常在内存中使用的数据结构完全差别(第三章中内存数据库更快的原因:省去了将内存数据结构编码为磁盘数据结构的开销)
语言特定的格式

编程语言对将内存对象编码为字节序列的支持:java中的java.io.Serializable,Python中的pickle,golang中的encoding/gob
存在题目:

  • 与特定的编程语言绑定
  • 为了规复相同对象范例的数据,解码过程必要实例化任意类的本领,这是安全题目的来源
  • 数据版本控制不方便,通常事后才考虑,忽略了前向后向兼容性带来的题目
  • 只得当临时使用,例如java其java.io.Serializable性能较差
JSON, XML,和二进制变体


  • XML和CSV不能区分数字和字符串,JSON固然能区分字符串和数字,但不区分整数和浮点数,而且不能指定精度
  • 处置处罚大量数据困难。大于\(2^{53}\)的整数不能再IEEE 754双精度浮点数中精确表示
  • JSON 和 XML 对 unicode(人类可读的文本)有很好的支持,但是不支持二进制。通过 base64 绕过这个限制。
  • CSV没有模式,应用步伐必要定义每行和每列的含义,格式模糊
二进制编码

JSON比XML简便,但与二进制格式相比还是太占空间,如今有很多二进制格式的 JSON(MessagePack,BSON,BJSON,UBJSON,BISON和Smile等)。
JSON,二进制编码长度为66
  1. {
  2.     "userName": "Martin",
  3.     "favoriteNumber": 1337,
  4.     "interests": ["daydreaming", "hacking"]
  5. }
复制代码

Thrift和Protocol Buffers


  • Protocol Buffers最初是在Google开发的,Thrift最初是在Facebook开发的,并且在2007~2008年都是开源的,都是二进制编码库
  • Thrift和Protocol Buffers都必要一个模式来编码任何数据
  • Thrift 有两种差别的二进制编码格式,分别称为 BinaryProtocol 和 CompactProtocol
    BinaryProtocol: 对上面的信息编码只必要59个字节。每个字段都有一个范例注释(指示是一个字符串,整数,列表等),还可以根据必要执行长度(字符串的长度,列表中的iterm数)。通过字段标签代替字段名

    CompactProtocol: 语义上等同于BinaryProtocol,相同信息打包只有34字节。会将字段范例和标签号打包到单个字节中,并使用可变长度证书来实现。将数字1337编码成2个字节,每个字节的最高为表示是否还有更多字节

Thrift
  1. struct Person {
  2.     1: required string       userName,
  3.     2: optional i64          favoriteNumber,
  4.     3: optional list<string> interests
  5. }
复制代码

  • Protocol Buffers:与Thrift的CompactProtocol相似,可以将相同信息打包到33字节中
Protocol Buffers
  1. message Person {
  2.     required string user_name       = 1;
  3.     optional int64  favorite_number = 2;
  4.     repeated string interests       = 3;
  5. }
复制代码

字段是否必需?对字段如何编码没有影响(二进制数据中没有任何字段指示是否必要字段)
字段标签和模式演变


  • 字段标记不能改变(否则导致现有的编码数据无效),字段名可以改变
  • 向前兼容:添加新的字段到架构,给每个字段新的标签号吗。就的代码读取新写入的数据,如果标签号码不能识别,简单忽略
  • 向后兼容:添加的每个字段必须是可选的或具有默认值的,否则之前的代码会检查失败
  • 删除字段:只能删除可选字段;不能再次使用相同的号码标签
数据范例和模式演变


  • 数据范例可以被改变:int32 升级 int64,新代码可以读取旧代码写入的数据(补0);但是旧代码不能剖析新数据(int32 读取 int64 会被截断)
  • Protobuf 一个细节:没有列表或数组范例,只有 repeated,因此可以把可选字段改为重复字段。读取旧数据的新代码会看到一个包含零个或一个元素的列表。读取新数据的旧代码只能那个看到列表的末了一个元素
  • Thrift 不能把更改为列表参数,但优点是可以嵌套列表
Avro

Avro是作为Hadoop的子项目在2009年开始的,因为Thrift不得当Hadoop的用例。也使用模式来指定正在编码的数据的结构。 它有两种模式语言:一种(Avro IDL)用于人工编辑,一种(基于JSON),更易于机器读取。
Avro IDL编写的示例模式
  1. record Person {
  2.     string                userName;
  3.     union { null, long }  favoriteNumber = null;
  4.     array<string>         interests;
  5. }
复制代码
等价的JSON表示
  1. {
  2.     "type": "record",
  3.     "name": "Person",
  4.     "fields": [
  5.         {"name": "userName", "type": "string"},
  6.         {"name": "favoriteNumber", "type": ["null", "long"], "default": null},
  7.         {"name": "interests", "type": {"type": "array", "items": "string"}
  8.     ]
  9. }
复制代码

  • 没有标签号吗,仅32个字节长,是全部编码中最近凑的。并且编码只是连在一起的值,不能识别字段和数据范例

  • 必须按照顺序遍历字段才能解码
  • 编解码必须使用完全相同的模式
Writer模式和Reader模式


  • Avro的关键头脑是Writer模式和Reader模式不必是相同的 - 他们只必要兼容
  • 数据读取的时候,会对比 Writer模式 和 Reader模式 的字段,然后就知道怎么读了

模式演变规则


  • 为了保持兼容性,只能添加或删除具有默认值的字段
  • 如果要添加一个没有默认值的字段,新的阅读器将无法读取旧作者写的数据,所以会破坏向后兼容性。如果要删除没有默认值的字段,旧的阅读器将无法读取新作者写入的数据,因此会打破兼容性
  • Avro不包含任何标签号码,因此对动态生成的模式更友善。因为使用Thrift或者PB必要手动写字段标签。而Avro在数据库发生变革时,可以直接生成新的Avro模式,导出数据,自动兼容
模式的优点


  • Protocol Buffers,Thrift和Avro都使用模式来描述二进制编码格式,比XML和JSON简单,也更支持更详细的验证规则
  • 基于模式的二进制编码相对于JSON,XML和CSV等文本数据格式的优点:

    • 它们可以比各种“二进制JSON”变体更紧凑,因为它们可以省略编码数据中的字段名称
    • 模式是一种有价值的文档情势,因为模式是解码所必需的,所以可以确定它是最新的(而手动维护的文档可能很容易偏离现实)
    • 维护一个模式的数据库答应您在摆设任何内容之前检查模式更改的向前和向后兼容性
    • 对于静态范例编程语言的用户来说,从模式生成代码的本领是有用的,因为它可以在编译时举行范例检查

数据流的范例

数据库中的数据流


  • 一般来说,会有多个进程访问数据库,可能会有某些进程运行较新代码、某些运行较旧的代码。因此数据库也经常必要向前兼容
  • 假设增长字段,那么较新的代码会写入把该值写入数据库。而旧版本的代码将读取记载,理想的举动是旧代码保持领域完整
  • 用旧代码读取并重新写入数据库时,有可能会导致数据丢失
在差别时间写入差别的值

架构演变答应整个数据库看起来好像是用单个模式编码的,即使底层存储可能包含用模式的各种汗青版本编码的记载
归档存储


  • 建立数据库快照,比如备份或者加载到数据堆栈:即使有差别时代的模式版本的混合,但通常使用最新模式举行编码
  • 由于数据转储是一次写入的,以后不变,所以 Avro 对象容器文件等格式非常得当
服务中的数据流:REST与RPC

Web服务

当服务使用HTTP作为底层通信协议时,可称之为Web服务
REST

  • 它夸大简单的数据格式,使用URL来标识资源,并使用HTTP功能举行缓存控制,身份验证和内容范例协商
  • 与SOAP相比,REST已经越来越受接待,至少在跨组织服务集成的背景下,并经常与微服务相关
  • 根据REST原则计划的API称为RESTful
    SOAP
  • SOAP是用于制作网络API请求的基于XML的协议
  • SOAP Web服务的API使用称为Web服务描述语言(WSDL)的基于XML的语言来描述。 WSDL支持代码生成,客户端可以使用当地类和方法调用(编码为XML消息并由框架再次解码)访问远程服务
  • 只管SOAP及其各种扩展外貌上是标准化的,但是差别厂商的实现之间的互操纵性每每会造成题目
RPC

RPC的缺陷

  • 当地函数调用是可预测的,并且成功或失败仅取决于受您控制的参数。而网络请求是不可预知的
  • 当地函数调用要么返回结果,要么抛出非常,或者永远不返回(因为进入无限循环或进程瓦解)。网络请求有另一个可能的结果:由于超时,它可能会返回没有结果。无法得知远程服务的响应发生了什么。
  • 如果响应丢失而出发请求充实,会导致该操纵被多次执行,必要引入幂等操纵
  • 调用当地函数时,可以高效地将引用(指针)通报给当地内存中的对象。当你发出一个网络请求时,全部这些参数都必要被编码成可以通过网络发送的一系列字节。如果参数是像数字或字符串这样的基本范例倒是没关系,但是对于较大的对象很快就会变成题目。
  • 客户端和服务端可以用差别的编程语言实现,RPC 框架必须把数据范例做翻译,可能会出题目
消息通报中的数据流

消息代理

RabbitMQ,ActiveMQ,HornetQ,NATS和Apache Kafka等消息队列

  • 消息代理通常不会执行任何特定的数据模型,消息知识包含一些元数据的字节序列,可以用任何编码格式
分布式的Actor框架


  • Actor模型是单个进程中并发的编程模型
  • 逻辑被封装在actor中,而不是直接处置处罚线程(以及竞争条件,锁定和死锁的相关题目)
  • 每个actor通常代表一个客户或实体,它可能有一些当地状态(不与其他任何角色共享),它通过发送和接收异步消息与其他角色通信。
  • 不保证消息传送:在某些错误情况下,消息将丢失。
  • 由于每个角色一次只能处置处罚一条消息,因此不必要担心线程,每个角色可以由框架独立调度
分布式Actor框架


  • 在分布式Actor框架中,此编程模型用于跨多个节点伸缩应用步伐。
  • 不管发送方和接收方是在同一个节点上还是在差别的节点上,都使用相同的消息通报机制。
  • 如果它们在差别的节点上,则该消息被透明地编码成字节序列,通过网络发送,并在另一侧解码
位置透明


  • 位置透明在actor模型中比在RPC中效果更好,因为actor模型已经假定消息可能会丢失,即使在单个进程中也是如此。
  • 只管网络上的延迟可能比同一个进程中的延迟更高,但是在使用actor模型时,当地和远程通信之间的基本不匹配是较少的

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

种地

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表