数据密集型应用系统的设计

目录

数据系统基础

数据模型与查询语言

数据编码与演化

概念

各类编码方式对比

数据流模式



数据系统基础

数据模型与查询语言

程序的核心是数据模型。

应用程序员观察周围的世界,通过对象和数据结构以及数据结构上的操作来对现实世界建模。存储这些数据结构要用通用数据模型,比如json文档数据模型,关系数据模型甚至图模型。

数据库程序员要考虑如何用内存,磁盘或网络字节来表示通用数据模型,以及提供一套简单的操作API。

硬件工程师需要考虑如何在各种硬件上实现字节,如电脉冲,光脉冲等。

每层都需要抽象一个简洁的数据模型和基于模型的API来对问题建模。

关系模型与文档模型

关系模型支撑了现在大部分的应用程序。首先是其提供的关系模型能很好的对业务问题进行建模并且规范化理论通过消除冗余来解决不一致的问题;另外对事物的支持也很重要,尤其是对于商务系统;当数据量变大的时候,索引又提供了不错的读写性能。

本节先从数据模型建模的角度对比关系模型和文档模型(以json为例)。

关系模型可以很简洁的表示一对多和多对多的关系,并且可以通过外键join起来进行联合查询。另外可以通过规范化理论消除重复来解决数据不一致的风险。但关系模型的问题是对象-数据不匹配,所以常常需要复杂的转化层,比如用来表示简历就很难受;并且面对超大数据集,超高的写入量其实扩展性不好,比如没人会用关系数据库存放训练集。

文档模型最大的优点是模式灵活性,跟对象模式上很近,所以表示简历很适合。文档数据库引入了引用,可以表示一对多和多对多的关系,但不如关系模型简洁。

文档数据库模式与对象贴近,局部性好,比如当训练集很合适。关系模型能更简洁的表示多对一和多对多的关系,join能力强 ,应对复杂情况可能更厉害。怎么简单怎么来,也可以联合使用它们。

数据查询语言

数据查询语言说的就是SQL,这是一种声明式编程。像java那种是命令式编程,每一步都要控制;如果写个sql则由优化器来控制整个过程如何执行。就像HiveSQL一样,写的每一个sql语句都会由存储引擎转化为MR任务,但对用户而言这提供了非常简洁的接口。

数据检索与存储

本节讨论数据存储格式和索引。索引是为了加快搜索引入的派生数据。

首先讨论一个key-value数据库的实现,已追加的方式来记录修改(快),同时通过HsahMap来建立索引。但这种方式的挑战在于HashMap可能大过内存,日志可能用尽磁盘。索引将日志分段,内存中的日志达到阈值就存到磁盘作为一个段,作为存储段可以压缩与合并,这样就避免了无效记录用尽磁盘;同时为每个段建立哈希索引,这样Hashmap也不至于太大。搜索时倒着向前搜索HashMap就可以了,段不会太多。

现在对段结构加以限制,每个段必须保证段中的key有序排列并且每个key唯一,叫SSTable(string sorted)。对于内存中未写入磁盘的段,可以用红黑树组织保证这一特性,大小达到阈值就存储为SSTable。这种结构内存写入很快,跟追加无异;同时每个存储段是按key有序且唯一,那么我可以同时多路归并来加快压缩合并的效率,这样段不会太多;为每个段建立哈希索引时不是每个key都需要维护,每几千个字节一个就可以了,可以让HashMap稀疏一些。读取时先去红黑树读,再往前找下一个索引读下一个段。这种结构使key-value数据库表现非常好。

数据编码与演化

概念

系统演进过程中需要滚动升级,那么就必须保证新代码可以读取旧模式的数据(向后兼容),并且新模式的数据可以被旧代码读取(向前兼容)。

程序中数据有两种表示形式,一种是内存中基于指针的各种数据结构(数组,列表,树等),另一种是文件和网络中的字节流。内存到文件是编码,文件到内存是解码。

本章主要讨论各种编码方式及其向前向后兼容性。

各类编码方式对比

编码方式示例优点问题
语言特定格式java.io.Serializable

  • 与特定语言绑定
  • 有安全问题

JSON和XML

(文本数据格式)

数据交换格式,尤其是JSON跨编程语言,并且相对简单
  • 数字编码有模糊之处
  • 对Unicode支持很好,但不支持二进制字符串,需要使用Base64将二进制编码为字符串
  • 二进制编码时需要把字段名称编码进去,压缩效果不好

Thtift和Protocol Buffers

(带模式的二进制编码)

RPC中使用Thrift编码格式

  • 编码时只编码前面的数字和类型,不编码名称,压缩效果很好
  • 基于IDL可以生成各种语言对应的代码
  • 当代码读取数据时,如果有不认识的标签号直接抛掉,如果数据比代码少一个字段,只要不是requied也没关系,所以无论新版本是增加还是删除字段,都有很好的向前向后兼容性(在IDL中的requied光影响生成的代码,不影响数据编码)
  • 改变类型时注意丢失精度
AvroHadoop

数据流模式

本节主要讨论进程之间数据流动的方式以及它们的向前向后兼容性,常见的有三种:

  • 基于数据库
  • 基于服务调用(RPC和REST)
  • 基于异步消息传递

基于数据库

向数据库中写入内容可以认为是向未来的自己发送消息。写是编码,读是解码。

考虑其向后兼容性,新代码很容易就能读取旧模式的数据。同时新模式的数据应该能被旧代码读取。但有一个问题,旧版本的应用程序更新新模式的数据会导致数据丢失。

基于服务调用

微服务体系架构:微服务体系下,每个功能模块都会独立部署和演进。当一个服务需要调用另一个服务的功能时,发一个请求即可。

通常在数据中心内部会使用RPC调用,与外部系统交互则会使用REST来处理。

既然微服务下的每个服务要独立演进和部署,那么向前向后兼容性就很重要。在客户端SDK读取服务端数据时,要保证旧代码读取新数据,有向前兼容性;在客户端数据写入服务器时,要保证旧数据能被新代码识别,有向后兼容性。

基于异步消息传递

通过异步消息队列传递数据流,既有RPC调用低延迟响应的特点,也像数据库一样通过一个媒介(消息队列)来进行传递。同时消息队列还常常用作缓冲区和解耦的用途。

Tip:在实际使用过,经常会把异步消息队列和其他两种方式结合使用;例如在包含多个状态节点的过程中,用队列来维护状态流转,通过数据库来维护各个状态需要的元数据,通过服务调用来执行具体的处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值