您的网页浏览器版本过低。更新您的浏览器,以便在该网站上获得更安全、更快速以及更优质的体验
建议升级浏览器: firefox 火狐浏览器 download chrome 谷歌浏览器 download

万万没想到,分布式存储系统的一致性是......

作者: 上海金融信息行业协会
2018-03-27 17:00
分布式存储系统是一个非常古老的话题,也是分布式系统里最难、最复杂、涉及面最广的问题之一。

点击"上海金融信息行业协会"关注我们

 

摘要:

分布式存储系统是一个非常古老的话题,也是分布式系统里最难、最复杂、涉及面最广的问题之一。

 

分布式存储系统模型

 

在分布式存储系统(包括OceanBase这样的分布式数据库)的使用中,我们经常会提到“一致性”这个词,但是这个术语在不同的系统、不同人的心目中有不同的内涵,很容易造成混淆。

 

想象一个最简单的存储系统,只有一个客户端(单进程)和一个服务端(单进程服务)。客户端顺序发起读写操作,服务端也顺序处理每个请求,那么无论从服务器视角还是从客户端视角,后一个操作都可以看到前一个操作的结果。然后,系统变的复杂一些,系统还是单个服务进程(单副本),但是有多个客户端并发进行操作。

 

然后,系统向另外一个方向变的复杂一些,为了让后端存储系统更健壮(目的不仅如此),我们可以让两个不同的服务进程(位于不同的机器上)同时存储同一份数据的拷贝,然后,通过某种同步机制让这两个拷贝的数据保持一致。这就是我们所说的“多副本”。

 

结合前两种模型,在一类真实的系统中,实际存在多个同时执行读写操作的客户端同时读写多个副本的后端存储服务。更进一步,前面的系统模型中,假设每个服务进程拥有和管理着一份“全量”的数据。而更复杂的系统中,每个服务进程中,只服务整个数据集的一个子集。

 

综上,我们要讨论的通用的分布式存储系统具有如下特性:

 

  1. 数据分为多个分片存储在多台服务节点上

  2. 每个分片有多个副本,存储在不同的服务节点上

  3. 许多客户端并发访问系统,执行读写操作,每个读写操作在系统中需要花费不等的时间

  4. 除非下文中特别注明和讨论,读写操作是原子的

 

与数据库事务一致性的区别

 

数据库事务的ACID的中也有一个一致性(consistency),但彼一致性非此一致性。ACID中的一致性是指,数据库的事务的执行,或者说事务观察到的数据,总是要满足某些全局的 一致性 约束条件,如唯一性约束,外键约束等。这个概念和数据库的数据是否多副本没关系。而本文的一致性在多副本的语境下才有意义。所以,数据库事务的一致性,是指数据项之间总是满足某些约束条件,或者说整个数据库在满足约束条件的意义上是 正确 的。

 

更让人崩溃的是,事务的隔离性也容易和这里的一致性混淆,因为它和一致性模型类似,限定了某种并发操作的执行顺序。事务的隔离性是指并发执行的事务,能以多大的程度看到看到彼此。这个概念也和数据是否多副本没有关系,单副本的单机数据库也需要支持不同的隔离级别。

 

客户端视角一致性模型

 

在多副本的存储系统中,无论采用什么样的多副本同步协议,为了保证多个副本能够一致,本质上都要求做到:

 

  1. 同一份数据的所有副本,都能够接收到全部写操作(无论需要花费多久时间)

  2. 所有副本要以某种确定顺序执行这些写操作

     

     

客户视角的一致性模型定义了下面4种不同的保证。

 

  1. 单调读。如果一个客户端读到了数据的某个版本n,那么之后它读到的版本必须大于等于n。

  2. 读自己所写。如果一个客户端写了某个数据的版本n,那么它之后的读操作必须读到大于等于版本n的数据。

  3. 单调写。单调写保证同一个客户端的两个不同写操作,在所有副本上都以他们到达存储系统的相同的顺序执行。单调写可以避免写操作被丢失。

  4. 读后写。读后写一致性,保证一个客户端读到版本n数据后(可能是其他客户端写入的),随后的写操作必须要在版本号大于等于n的副本上执行。

 

系统对外提供的不同的一致性级别,实际上提供了这其中某几个保证。不同的一致性级别,限定了系统允许的操作执行顺序,以及允许读到多旧的数据。

 

Cosmos DB的一致性级别

 

Azure Cosmos DB是一个支持多地部署的分布式NoSQL数据库服务。它提供了丰富的可配置的一致性级别。以下五种一致性级别,从前向后可以提供更低的读写延迟,更高的可用性,更好的读扩展性。

 

  1. 强一致性

  • 保证读操作总是可以读到最新版本的数据(即可线性化)

  • 写操作需要同步到多数派副本后才能成功提交。读操作需要多数派副本应答后才返回给客户端。读操作不会看到未提交的或者部分写操作的结果,并且总是可以读到最近的写操作的结果。

  • 保证了全局的(会话间)单调读,读自己所写,单调写,读后写

  • 读操作的代价比其他一致性级别都要高,读延迟最高

  • 有界旧一致性(bounded staleness)

  • 保证读到的数据最多和最新版本差K个版本

  • 通过维护一个滑动窗口,在窗口之外,有界旧一致性保证了操作的全局序。此外,在一个地域内,保证了单调读。

  • 会话一致性

  • 在一个会话内保证单调读,单调写,和读自己所写,会话之间不保证

  • 会话一致性把读写操作的版本信息维护在客户端会话中,在多个副本之间传递

  • 会话一致性的读写延迟都很低

  • 前缀一致性

  • 前缀一致保证,在没有更多写操作的情况下,所有的副本最终会一致

  • 前缀一致保证,读操作不会看到乱序的写操作。例如,写操作执行的顺序是`A, B, C`,那么一个客户端只能看到`A`, `A, B`, 或者`A, B, C`,不会读到`A, C`,或者`B, A, C`等。

  • 在每个会话内保证了单调读

  • 最终一致性.

  • 最终一致性保证,在没有更多写操作的情况下,所有的副本最终会一致

  • 最终一致性是很弱的一致性保证,客户端可以读到比之前发生的读更旧的数据

  • 最终一致性可以提供最低的读写延迟和最高的可用性,因为它可以选择读取任意一个副本

 

Cosmos DB的文档中提到了一个有趣的数字。大约有73%的用户使用会话一致性级别,有20%的用户使用有界旧一致性级别。

 

Cassandra的一致性级别

 

Cassandra是一个使用多数派协议的NoSQL存储系统,通过控制读写操作访问的副本数和副本的位置,可以实现不同的一致性级别。注意,作为NoSQL系统,Cassandra只提供单行操作的原子性,多行操作不是原子的。下面的读写操作,都是指单行操作。

 

对于NoSQL系统,一般支持的写操作叫做PUT(有些系统叫做UPSERT)。这个操作的含义是,如果这行存在(通过唯一主键查找),则修改它;如果这行不存在,则插入。这个语义,可以近似(在不考虑二级索引的时候)等价于关系数据库的INSERT ON DUPLICATE KEY UPDATE语句。这个语义有什么特殊之处呢? 第一, 它是幂等的 。所以PUT操作可以重复执行,不怕消息重传。第二, 它是覆盖(overwrite)语义 。所以,NoSQL系统的最终一致性,允许对于同一行数据的写操作可以乱序,只要写操作不断,最终各个副本会一致。而关系数据库的insert和update等修改语句,内部实现都是即需要读也需要写。

 

写操作配置

 

写操作一致性配置定义了对于写操作在哪些副本上成功之后,才能返回给客户端。

 

  • ALL: 写操作需要同步到所有副本并应用到内存中。提供了最强的一致性保证,但是单点故障会引起写入失败,造成系统不可用。

  • EACH_QUORUM: 在每个机房(数据中心)中,写操作同步到多数派副本节点中。在多数据中心部署的集群中,可以在每个数据中心提供QUORUM一致性保证。

  • QUORUM: 写操作同步到多数派副本节点中。当少数副本宕机的时候,写操作可以持续服务。

  • LOCAL_QUORUM: 写操作必须同步到协调者节点所在数据中心的多数派副本中。这种模式可以避免多数据中心部署时,跨机房同步引起的高延迟。在单机房内,可以容忍少数派宕机。

  • ONE: 写操作必须写入最少一个副本中。

  • TWO: 写操作必须写入至少两个副本中。

  • THREE: 写操作必须写入至少三个副本中。

  • LOCAL_ONE: 写操作必须写入本地数据中心至少一个副本中。在多机房部署的集群中,可以达到和ONE相同的容灾效果,并且把写操作限制在本地机房。

 

读操作配置

 

每个读操作可以设定如下不同的一致性配置。

 

  • ALL: 读操作在全部副本节点应答后才返回给客户端。单点单机会引起写操作失败,造成系统不可用。

  • QUORUM: 读操作在多数派副本返回应答后返回给客户端。

  • LOCAL_QUORUM: 读操作在本机房多数派副本返回应答后返回给客户端。可以避免跨机房访问的高延迟。

  • ONE: 最近的一个副本节点应答后即返回给客户端。可能返回旧数据。

  • TWO: 两个副本节点应答后即返回给客户端。

  • THREE: 三个副本节点应答后返回给客户端。

  • LOCAL_ONE: 本机房最近的一个副本节点应答后返回客户端。

 

系统一致性级别

 

从系统层面来看,Cassandra提供了强一致性和最终一致性两种一致性级别。不考虑多机房因素,通过设置上述读写操作的一致性配置,当写入副本数与读取副本数之和大于总副本数的时候,可以保证读操作总是可以读取最新被写入的数据,即强一致性保证。如果写入副本数与读取副本数之和小于总副本数的时候,读操作可能无法读到最新的数据,而且读操作可能读到比之前发生的读操作更旧的数据,所以这种情况下是最终一致性。

 

而副本位置是选择整个集群、每个机房还是本地机房等因素,是为了在不同的容灾场景下,对跨机房通讯引入的高延迟进行优化,固有的一致性级别并不受影响。

 

OceanBase的一致性级别

 

一般来说,NoSQL类数据库,比如HBase, Cassandra等,仅提供单行操作的原子性保证。而关系数据库的基本操作是一条SQL语句,SQL语句天生是多行操作,而且支持多语句事务和事务的回滚等,在SQL语句级和事务级还都需要提供原子性保证。可以理解,实现相同的一致性级别,分布式关系数据库比NoSQL类系统的复杂度和代价都要高。

 

OceanBase使用Multi-Paxos分布式共识算法在多个数据副本之间同步事务提交日志,每个修改事务,要在多数派副本应答以后才认为提交成功。多个副本之间,通过自主投票的机制,选出其中一个副本为主副本(leader),它负责所有修改语句的执行,特别的,达成多数派的事务提交日志要求包含主副本自己。

 

如果一个语句的执行涉及到多个表的分区,在OceanBase中这些分区的主副本可能位于不同的服务节点上。严格的数据库隔离级别要求涉及多个分区的读请求看到的是一个“快照”,也就是说,不允许看到部分事务。这要求维护某种形式的全局读版本号,开销较大。如果应用允许,可以调整读一致性级别,系统保证读到最新写入的数据,但是不同分区上的数据不是一个快照。从一致性级别来看,这也是强一致性级别,但是打破了数据库事务的ACID属性。

 

在使用数据库的互联网业务中,有很多情况下业务组件还允许读到稍旧的数据,OceanBase提供两种更弱的一致性级别。在最弱的级别下,我们可以利用所有副本提供读服务。在OceanBase的实现中,多副本同步协议只保证日志落盘,并不要求日志在多数派副本上完成回放(写入存储引擎的memtable中)。

 

但是,实际上,使用关系数据库的应用,大多数还是不能容忍乱序读的。通过在数据库连接内记录读版本号,我们还提供了比最终一致性更严格的前缀一致性。它可以在每个数据库连接内,保证单调读。这种模式,一般用于OceanBase集群内读库的访问,业务本身是读写分离的架构。

 

此外,对于这两种弱一致性级别,用户可以通过配置,控制允许读到多旧的数据。在多地部署OceanBase的时候,跨地域副本数据之间的延迟是固有的。比如,用户配置允许读到30秒内的数据,那么只要本地副本的延迟小于30秒,则读操作可以读取本地副本。如果不能满足要求,则读取主副本所在地的其他副本。如果还不能满足,则会读取主副本。这样的方式可以获得最小的读延迟,以及比强一致性读更好的可用性。这样,在同时保证会话级单调读的条件下,我们提供了有界旧一致性级别。

 

综上所述,OceanBase在保证关系数据库完备的ACID事务语义前提下,提供了强一致性、有界旧一致性、前缀一致性和最终一致性这几种一致性级别。

海纳百川,追求卓越

开明睿智,大气谦和

 

上海金融信息行业协会

官网:http://sfia.org.cn/

欢迎大家猛戳二维码,关注我们

-------------------------------------

如果您有优质的、符合见闻调性的原创文章,欢迎以个人的名义投稿入驻华尔街见闻名家专栏。

投稿方式 :请将个人简介以及代表作品发送至 zhuanlan@wallstreetcn.com ,并附上电话和微信以便做进一步沟通,在主题中标明: 申请入驻见闻专栏 + 投稿人名字

参与评论
收藏
qrcode
相关资讯
参与评论