分布式数据库的强一致性

小杜的书房 2022-02-14
264
哈喽 大家好,我是小杜 欢迎来到我的书房做客。在上一篇文章的最后我给大家留了一个思考话题,那就是数据库的ACID特性与分布式系统的CAP理论中,都代表一致性的"C"是否具有相同的作用?
一直以来,在分布式系统和数据库两个工程应用中,一致性(Consistency)都具有重要的作用,但是这两个"C"所表达的作用却并不相同。
ACID特性: 在ACID特性中,“C”代表经过事务处理后,数据保持完整性。在关系型数据库中,一致性与事务密切相关,又进一步细分到了ACID四个方面,其中“I”所代表的隔离性(Isolation)是一致性的核心内容,它所研究的就是如何协调事务之间的冲突。
CAP理论:在CAP理论中,“C”代表分布式系统的服务器之间以复制的方式拥有相同的数据。也就是说分布式系统中的一致性是在探讨系统内的一份数据存在多个物理的数据副本时,对其执行读写操作会产生什么样的结果。
根据上边的论述,当我们在谈论分布式数据库的一致性时。实质上是在谈论分布式数据库的数据一致性事务一致性两个方面。
这里推荐你阅读分布式数据库领域的经典论文,由威睿和三星的数据库专家发表在2016年IEEE Computer Society Technical Commitee on Data Engineering 会议上的论文 《The many faces of consistency》(一致性的多张面孔)。


数据一致性
在这篇微信文章中我们首先来介绍分布式数据库的数据一致性。在讨论数据一致性之前还需要一个前提,同时存在读写两种操作,否则是没有意义的。
把读数据和写数据两个因素加在一起,就是多副本数据的一组读写策略,被称为“一致性模型”(Cosistency Model)。一致性模型的种类很多,为了分析不同的模型,我们要分别从状态视角和操作视角进行分析。
状态视角:数据所处的客观实际状态,要保持一致性。
操作视角:用户通过协议约定的操作,能够读取到的数据保持一致。

状态视角
从状态视角来看,经过任何数据变更处理后,数据只存在两种状态,数据一致或者不一致。在NoSQL的BASE理论中,数据不一致仅仅是暂时的。而数据永远不一致的状态,几乎不可能应用于任何工程实践。
所以从习惯上来讲,不一致会被称为“弱一致性”,而一致会被称为“强一致性”。对于分布式系统来说,要求数据状态的“强一致”是没有意义的,下面我们以一个例子来说明。



现在有一个MySQL数据库集群,集群有一个主库和两个备库构成。集群开启同步复制模式,主库与备库之间同步binlog日志( binlog日志用于记录MySQL数据库的所有DML操作 )。在该模式下,主库只有收到两个备库的成功响应后,才会向客户端返回成功信息。
对于我们所说的栗子中的情况,用户收到MySQL集群返回的成功响应时,主库与备库之间的数据副本已经达成一致。然而该系统在任何企业的生产环境中几乎不会使用,主要原因如下:
(1)性能差:主库必须等到两个备库均返回成功后,才能向用户反馈提交成功。图中由于网络阻塞,“备库 2”稍晚于“备库 1”返回响应,增加了数据库集群整体的延时。
(2)可用性差:我们在上一篇微信文章中讲提到过可用性概念,任何设备都有可能出现故障,尤其是 x86 这样的通用商业设备,故障率会更高。但在全同步复制模式下,集群中的三个节点被串联起来,如果单台MySQL服务器的可用性是 95%,那么集群整体的可用性就是 85.7%(95%*95%*95%=85.7%),跟单机相比反而降低了。
可想而知,如果备库1或者备库2任何一个MySQL服务器出现故障,整个集群将一直处于网络阻塞状态,客户端无法得到MySQL集群的成功响应。
因此在分布式系统中,为了解决可用性与数据强一致性之间的矛盾,很多产品选择了弱一致性。

NoSQL 产品就是应用弱一致性的典型代表,但对弱一致性的接受仍然是有限度的,这就是 BASE 理论中的 E 所代表的最终一致性(Eventually Consistency)。
对于最终一致性,可以这样理解:在主副本执行写操作并反馈成功时,不要求其他副本与主副本保持一致,但在经过一段时间后这些副本最终会追上主副本的进度,重新达到数据状态的一致。
对于最终一致性会存在一个疑问,那就是“经过一段时间”到底是多长时间?这就需要从上边提到的操作视角来进行分析。

操作视角
"最终"一词有些含糊不清,总的来说副本落后的程度在理论上没有上限。当数据滞后时间太长时,导致的数据不一致不仅仅是一个理论问题,还是一个实实在在的系统应用问题。
由于BASE理论中最终一致性的存在,使得在分布式系统领域出现了众多的一致性模型。下面小杜来为大家介绍下来自英国剑桥大学计算机科学系,分布式系统方向的大牛Martin Kleppmann 在其著作《Designing Data-Intensive Application》中所提到的几种广泛应用于工程实践的一致性模型。



 “写后读一致性”(Read after Write Consistency)
  首先来说“写后读一致性”(Read after Write Consistency),它也称为“读写一致性”,或“读自己写一致性”(Read My Writes Consistency)。虽然名字有些奇怪,但它却最准确地描述了这种一致性模型的使用效果。
  举个栗子来说明:
  小明喜欢平时发朋友圈来记录自己的生活,这天小明参加了同学聚会,会后在朋友圈分享了自己在同学聚会上的照片。小明知道自己的同学也会看到他分享的照片,特意又刷新了下朋友圈,确认照片分享成功,小明十分满意。
你是否意识到了这个过程中就实现了“写后读一致性”,我画了一张流程图来进一步说明。

小明发布照片的延时极短,用户体验很好。这是因为数据仅被保存在主副本 R1 上,就立即反馈保存成功。而其他副本在后台异步更新,由于网络的关系每个副本更新速度不同,在 T2 时刻两个副本达成一致。从过程来看,这符合前面所讲的最终一致性。
要特别注意的是,小明有一个再次刷新朋友圈的动作,这时如果访问副本 R2,由于其尚未完成同步,聚会照片将会消失,小明就会觉得自己的照片被弄丢了。此处,我们假定系统可以通过某种策略由写入节点的主副本 R1 负责后续的读取操作,这样就实现了写后读一致性,可以保证小明再次读取到照片。
自己写入成功的任何数据,下一刻一定能读取到,其内容保证与自己最后一次写入完全一致,这就是“写后读一致性”、“读自己一致性”名字的由来。

 “单调一致性”(Monotonic Read Consistency)
 接着上面的话题我们接着讲,小明发完朋友圈后,他的同学小刚可以看到朋友圈内容么?会不会存在什么异常?
我告诉你,有可能会出现“时光倒流”的现象!
假设此时小刚也在刷朋友圈,看到了小明分享的同学聚会照片。这时小刚接了一个电话,又回到朋友群再次刷新,发现刚才看到的小明的照片不见了!小刚一脸懵,而小明却并没有删除照片。这里究竟出现了什么异常呢?

在小明发布照片后的瞬间,小刚也刷新了朋友圈,此时读取到副本 R1,所以小刚看到了照片;片刻之后,小刚再次刷新,此时读取到的副本是 R2,而副本R2还没有完成从主副本R1的数据复制,于是照片就消失了。小刚以为小明删除了照片,但实际上这完全是程序错误造成的,数据向后回滚,出现了“时光倒流”的现象。
想要排除这种异常,系统必须实现单调一致性(Monotonic Read Consistency)。单调一致性保证了用户在读取到某个值后,不会出现比这个值更旧的值。
在工程中实现单调一致性,可以将用户与副本建立固定的映射关系,比如使用哈希算法(哈希在分布式数据库中具有重要的作用,我还打算写一篇数据分片的文章和大家进一步探讨哈希算法)将用户 ID 映射到固定副本上,这样避免了在多个副本中切换,也就不会出现上面的异常了

 “前缀一致性”( Consistent Prefix )
  前缀一致性是通过显式的方式来保证因果性。通过对原有数据添加后缀来控制进程读取数据的顺序。
   这里我们来具体讲述一个例子来解释前缀一致性的作用:
   这天家住北京的小明在通过CCTV5观看NBA比赛 洛杉矶湖人 vs 密尔沃基雄鹿的比赛,在比赛中场时间作为湖人队球迷的小明发了一条微信朋友圈 “Go Lakers”。这时家在北京的小刚在手机上刷到了小明的这条朋友圈信息,并在下边留言“现在的比分是多少?”,小明回复:“96:82 湖人领先”。
 远在美国的小王这时却看到了一个奇怪的现象,评论区先出现了小明的回复“96:82 湖人领先”,而后才出现小刚的评论“现在的比分是多少?”
 显然小刚的问题和小明的回答之间具有因果关系,但由于数据复制时忽略了这层因果关系,以及分布式系统内部网络上的延迟,于是就出现了这种异常。
保持这种关系的一致性称为前缀一致性。
这里不得不吐槽下我司目前使用的内部办公通讯软件,就会出现如上所述的问题。
 
 “线性一致性”( Linearizability )
  线性一致性是最强的一种一致性模型,它是建立在事件的先后顺序之上的。在线性一致性下,整个系统表现得好像所有操作被记录在一条时间线上,并且被原子化,这样任意两个事件都可以比较先后顺序。
但是,集群中的各个节点不能做到真正的时钟同步,这样节点有各自的时间线。那么,如何将操作记录在一条时间线上呢?这就需要一个绝对时间,也就是全局时钟
这里不得不提一下谷歌公司的分布式数据库Spanner。Spanner以全球化部署为目前,为了保证分布式数据库的大范围部署,利用了GPS和原子钟进行授时。谷歌将自己的这项独门绝技称为True Time,可以保证在全球范围内部署的任意节点能同时获得一个绝对时间,设计理论误差在7ms以内。


对于线性一致性,学术界其实是有争议的。根据爱因斯坦的相对论,“时间是相对的”,没有绝对时间,也就不存在全序的事件顺序,不同的观察者可能对于哪个事件先发生是无法达成一致的。因此,线性一致性是有局限性的。

从工程角度看,因为我们的应用场景都在经典物理学的宏观、低速的范围内,因此线性一致性也是适用的。

 “因果一致性”( Causal Consistency )
 因果一致性的基础是偏序关系,也就是说,部分事件顺序是可以比较的。至少一个节点内部的事件是可以排序的,依靠节点的本地时钟就行了; 节点间如果发生通讯,则参与通讯的两个事件也是可以排序的,接收方的事件一定晚于调用方的事件。
基于这种偏序关系,Leslie Lamport 在论文《Time, Clocks, and the Ordering of Events in a Distributed System》中提出了逻辑时钟的概念。
  


Leslie Lamport对计算机科学尤其是分布式计算领域做出了杰出的贡献,于2013年荣获图灵奖。在之后的微信文章中,小杜还会提到这位技术大师。Lamport对数据库时钟、拜占庭将军问题、古希腊议会选举问题、以及Paxos算法进行了大量的开创性研究。
借助逻辑时钟仍然可以建立全序关系,虽然这个全序关系是不够精确的。但因果一致性弱于线性一致性,在并发性能上具有优势,也足以处理多数的异常现象,所以因果一致性也在工业界得到了应用。
具体到分布式数据库领域,CockroachDB 和 YugabyteDB 都在设计中采用了逻辑混合时钟(HLC, Hybrid Logical Clocks),这个方案源自 Lamport 的逻辑时钟,也取得了不错的效果。      

在本篇文章中,我们所提到的一致性模型是有一定强弱关系的。排序关系如下:
线性一致性 >  因果一致性 > { 写后读一致性,单调一致性,前缀一致性 }

本次文章的最后,小杜想给大家留一个思考话题。本次文章我们主要介绍了几个常用的一致性模型,而你可能知道Paxos算法被称为一致性协议,那么“一致性协议”和数据一致性之间是什么关系呢?欢迎你的留言。








文章转载自小杜的书房,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论