zoukankan      html  css  js  c++  java
  • 海量存储之十六–一致性和高可用专题、十七

    很久木有和大家见面了,因为博主也需要时间来沉淀。。博主也需要学习和思考。。

    好吧,不多废话,进入正题,今天我们谈的东西是一致性和安全性。

    一致性这个问题,非常绕,想用语言表述,难度很大,我给别人去讲的时候,一般都是白板,因为白板有类似“动画”的效果,能够帮助别人理解,但使用文字,就没有办法了,只好要求各位有一定的抽象思维能力,能在自己的脑袋里模拟这种动画吧:)

     

    主要会聊到: 简单的双机两阶段提交,hbase强一致三份,vector clock ,paxos思路,paxos改进思路,既然要阐述问题,那我们就需要先给自己画个框框,首先来对这个问题做一个定义。

    ============

    写在前面,发现不少读者都会用自己以前的2pc知识来套用文章里面提到的2pc改。

    我想说的是,在这里的两段提交,与大家所理解的两段提交,有一定区别。

    他是为了满足文中提到的C问题而改过的2pc.所以能够解决一些问题,但也会产生一些新的问题。各位一定要放弃过去对2pc的理解,一步一步的跟着文中的思路走下来,你就会发现他其实不是真正事务中所用到的2pc,而是专门为了同步和高可用改过的2pc协议。

    ============

    问题描述

     

    人们在计算机上面,经常会碰到这样一个需求:如何能够保证一个数据写入到某台或某组机器上,并且计算机返回成功,那么无论机器是否掉电,都能够保证数据不会丢失,并且能够保证数据按照我写入的顺序排列呢?

    面对这个问题,一般人的最常见思路就是:每次写都必须保证磁盘写成功才算成功就好了嘛。

     

    没错,这就是单机一致性的最好诠释,每次写入,都落一次磁盘,就可以保证在单机的数据安全了。

    那么有人问了,硬盘坏了怎么办?不是还丢了么?没错啊,所以又引入了一种技术,叫做磁盘阵列(这东西,在我们目前的这个一致性里面不去表述,感兴趣的同学可以自己去研究一下)

     

    好像说到这,一致性就说完了嘛。。无非也就是保证每次成功的写入,数据不会丢失,并且按照写入的顺序排列,就可以保证数据的一致性了。

     

    别急,我们这才要进入正题:

    如果给你更多的机器,你能做到更安全么?

    那么,我们来看看,有了更多机器,我们能做到什么?

    以前,单机的时候,这台机器挂了,服务也就终止了,没有任何方式能够保证在这台机器断电或挂了的时候,他还能服务不是?但如果有更多的机器,那么你就会忽然发现,一台机器挂了,不是还有其他机器么?一个机房里面的所有机器都挂了,不是还有其他机房么?美国被核武器爆菊了以后,不是还有中国的机房么?地球被火星来客毁灭了,不是还有火星机房么?

     

    哈,排比了这么多,其实就是想说明,在机器多了以后,人们就可以额外的追求更多的东西了,这东西就是服务的可用性,无论如何,只要有钱,有网络,服务就可用。怎么样?吸引人吧?(不过,可用性这个词在CAP理论里面,不只是指服务可以被访问,还有个很扯淡的属性是延迟,因为延迟这个属性很难被量化定义,所以我一般认为CAP是比较扯淡的。。。)

     

    好,我们现在就来重新定义一下我们要研究的问题:

    寻求一种能够保证,在给定多台计算机,并且他们相互之间由网络相互连通,中间的数据没有拜占庭将军问题(数据不会被伪造)的前提下,能够做到以下两个特性的方法:

    1)数据每次成功的写入,数据不会丢失,并且按照写入的顺序排列

    2)给定安全级别(美国被爆菊?火星人入侵?),保证服务可用性,并尽可能减少机器的消耗。

    我们把这个问题简写为C问题,里面有两个子问题C1,C2.

     

    为了阐述一下C问题,我们需要先准备一个基础知识,这知识如此重要而简单,以至于将伴随着每一个分布式问题而出现(以前,也说过这个问题的哦..:) )

    假定有两个人,李雷和韩梅梅,假定,李雷让韩梅梅去把隔壁班的电灯关掉,这时候,韩梅梅可能有以下几种反馈:

    1)”好了,关了”(成功)

    2)”开关坏了,没法关”(失败)

    3)

     

    呵呵,3是什么?韩梅梅被外星人劫持了,消失了。。于是,反馈也没有了。。(无反馈)

    这是一切网络传递问题的核心,请好好理解哈。。。

     

    ————–准备结束,进入正题———————

     

    两段提交改:

     

    首先,我们来看一种最容易想到的方式,2pc变种协议。

    如果我有两台机器,那么如何能够保证两台机器C问题呢?

     

     

    海量存储之十六--一致性和高可用专题

     

    我们假定A是协调者,那么A将某个事件通知给B,B会有以下几种反馈:

    1.成功,这个可以不表。正常状态

    2.失败,这个是第二概率出现的事件,比如硬盘满了?内存满了?不符合某些条件?为了解决这个情况,所以我们必须让A多一个步骤,准备,准备意味着如果B失败,那么A也自然不应该继续进行,应该将A的所有已经做得修改回滚,然后通知客户端:错误啦。

    因此,我们为了能做到能够让A应付B失败的这个情况,需要将同步协议设计为:

    PrepareA -> Commit B -> Commit A.

    使用这个协议,就可以保证B就算出现了某些异常情况,数据还能够回滚。

     

    我们再看一些异常情况,因为总共就三个步骤,所以很容易可以枚举所有可能出现的问题:

    我们将最恶心的一种情况排除掉,因为网络无反馈导致的问题,看看其他问题。

    PA ->C B(b机器挂掉): 也就是说,如果在Commit B这个步骤失败,这时候可以很容易的通过直接回滚在A的修改,并返回前端异常,来满足一致性问题,但可用性有所丧失,因为这次写入是失败的。

    在这时的可用性呢? B机器挂掉,对A来说,应该允许提交继续进行。这样才能保证服务可用,否则,只要有任意的一个机器挂掉,整个集群就不可用,这肯定是不符合预期的嘛。

    PA -> C B -> C A(A机器挂掉) :这种情况下,Commit A步骤失败,应该做的事情是,在A这个机器重新恢复后,因为自己的状态是P A,所以他必须询问B机器,你提交了没有啊?如果B机器回答:我提交成功了,那么A机器也必须将自己的数据也做提交操作,就能达到一致。

    在可用性上面,一台机器挂掉,另外一台还是可以用的,那么,自然而然的想法是,去另外一台机器上做尝试。

    从上面可以看到,因为B机器已经提交了这条记录,所以数据已经是最新了,可以基于最新数据做新的提交和其他操作,是安全的。

     

     

    怎么样?觉得绕不绕?不过还没完呢,我们来看看2pc改的死穴在哪里。。

    还记得刚开始的时候,我们提到了排除掉了一种最恶心的情况,这就是网络上最臭名昭著的问题,无反馈啊无反馈。。

    无反馈这个情况,在2pc改中只会在一个地方出现,因为只有一次网络传输过程:

    A把自己的状态设置为prepare,然后传递消息给B机器,让B机器做提交操作,然后B反馈A结果。这是唯一的一次网络调用。

     

    那么,这无反馈意味着什么呢?

    1.B成功提交

    2.B 失败(机器挂掉应该被归类于此)

    3.网络断开

     

    更准确的来说,其实从A机器的角度来看这件事,有两类事情是无法区分出来的:

    1)B机器是挂掉了呢?还是只是网络断掉了?

    2)要求B做的操作,是成功了呢?还是失败了呢?

    不要小看这两种情况。。。他意味着两个悲剧的产生。

     

    首先,一致性上就出现了问题,无反馈的情况下,无法区分成功还是失败了,于是最安全和保险的方式,就是等着。。。没错,你没看错,就是死等。等到B给个反馈。。。这种在可用性上基本上是0分了。。无论你有多少机器,死等总不是个办法。。

    然后,可用性也出现了个问题,我们来看看这个著名的“脑裂”问题吧:

    A得不到B的反馈,又为了保证自己的可用性,唯一的选择就只好像【P A ->C B(b机器挂掉):】这里面所提到的方法一样:等待一段时间,超时以后,认为B机器挂掉了。于是自己继续接收新的请求,而不再尝试同步给B。又因为可用性指标是如此重要,所以这基本成为了在这种情况下的必然选择,然而,这个选择会带来更大的问题,左脑和右脑被分开了!

    为什么?我们假定A所在的机房有一组client,叫做client in A. B 机房有一组client 叫做client in B。开始,A是主机,整个结构worked well.

     

    海量存储之十六--一致性和高可用专题

     

     

     

    一旦发生断网

     

    海量存储之十六--一致性和高可用专题

     

     

    在这种情况下,A无法给B传递信息,为了可用性,只好认为B挂掉了。允许所有client in A 提交请求到自己,不再尝试同步给B.而B与A的心跳也因为断网而中断,他也无法知道,A到底是挂掉了呢?还是只是网络断了,但为了可用性,只好也把自己设置为主机,允许所有client in B写入数据。于是。。出现了两个主机。。。脑裂。

     

    这就是两段提交问题解决了什么,以及面临了什么困境。

    碰到问题,就要去解决,所以,针对一致性问题上的那个“死等”的萌呆属性,有人提出了三段提交协议,使用增加的一段提交来减少这种死等的情况。不过3PC基本上没有人在用,因为有其他协议可以做到更多的特性的同时又解决了死等的问题,所以3pc我们在这里就不表了。3pc是无法解决脑裂问题的,所以更多的人把3pc当做发展过程中的一颗路旁的小石头。。

     

    而针对脑裂,最简单的解决问题的方法,就是引入第三视点,observer。

    既然两个人之间,直接通过网络无法区分出对方是不是挂掉了,那么,放另外一台机器在第三个机房,如果真的碰到无响应的时候,就去问问observer:对方活着没有啊?就可以防止脑裂问题了。但这种方法是无法解决一致性问题中的死等问题的。。。

     

    所以,最容易想到的方式就是,3pc+observer,完美解决双机一致性和安全性问题。

     

    后记3317字。nnd我本来以为可以5篇儿纸说完这个问题的。。现在发现刚阐述了很小一部分。。果然一致性和可用性真不是个简单的问题。这个做个专题吧。


    ——-三段提交改———–

    回顾上文,我们已经提到了,在两段提交协议里面有个“死等”的过程,那么我们来看看三段提交协议是怎么解决这个问题的,需要注意的是,3pc只是解决了死等问题,对脑裂没有贡献。用的也不多,我们只把它当做路边的小石头,理解了作为模型的一种,参考一下就行了。

    海量存储之十七--一致性和高可用专题

    首先分析原因,死等的关键,在于B机器挂了,A机器没有收到B机器的反馈。这时候A不知道应该怎么办,所以只能死等。否则都可能造成不一致。

    在这里,我要着重强调一下,3pc的假定是,你能确切的知道B是机器挂掉了,不是网络断开了(虽然在A看来,它无法区分这两种情况,我们在开始已经提到)。

    3pc解决B在commit B这个阶段挂掉方法,是做两次通信。

    增加了一次通信,叫pre commit【我们这里,因为B是跟随A的,不会在B出现写读冲突或者写写冲突,所以我们也可以减少B的一个prepare(canCommit)状态,我们把它叫做3pc!改!】

    下面,让我来说明一下这个问题:

    为了解决死等的问题,我们只有一种选择,就是让每次请求都有一个超时时间。也就意味着,每次请求都要有个计时器,计算多少时间以后,这个请求就算是超时了。

    但是,光有超时是不够的,在上一篇文章里面也提到,在2pc改中,请求超时可能意味着B提交成功了,或者B提交失败了。这时候A是无所适从的,提交也不是,不提交也不是。

    最简单的一种思路,其实大家经常用到:

    大家还记得在有些影片里面,某大boss要参加一场鸿门宴,于是跟下面的将军说:“我现在要去参加鸿门宴,不一定能回来,我们约定一下,如果今天晚上10点我还没回来,你就给我带军队平了他们,给我报仇。”

    这其实就是三段提交核心思想的真实写照,问题的关键就是约定延迟某时间后,最终双方就按照某种”先期约定”进行后面的操作。(在这个case,先期约定是对方出老千,我们出兵跟他拼命。在3pc内的先期约定是双方都算作提交成功)。

    那么,在这延迟的时间内,其实是数据状态是混沌的(或者说量子态的?笑),10点前,这boss是死了啊?还是活着?没人知道,这种混沌只有被揭开盖子的那个时刻(发送doCommit(),doAbrot(),boss派人你双方和解了,boss派人告诉你快来救我),才会变成决定论的。

    好,我们回到3pc上来。

    在第一次通信(pre commit)的时候,A和B的先期约定是如果某个时间后两方都没有后续反馈,那么算作提交成功。这里需要注意的是,没有后续反馈的原因,在3pc理论里只是指B挂掉这种情况,【而不是】指网络出现问题这个情况。

    这样,如果A机器发送pre commit这条信息,能够拿到这么几个反馈:

    1.成功

    2.失败

    3.超时【超时的原因是B挂掉,不是网络断开】

    而B呢?在收到precommit 这条信息之后,能够发送给A几种反馈

    1.成功

    2.失败

    【这里要非常注意,是”没有”超时这个实际会发生的选项的,因为3pc协议是排除了网络超时这种情况的】

    所以B的策略很简单,如果pre commit我反馈了成功给A,那么如果我发现在等待超时之后仍然没有获得A的提交或终止请求,那么我就提交。反之我就终止。

    这里的等待超时,我们来看看可能由哪些问题引起:

    1.因为A机器挂了,无法发送最终的提交或回滚命令给B

    2.因为B机器挂了,没有收到A机器的提交或回滚命令。

    再来看A这台协调机:

    如果preCommit 得到了失败或超时的结果,那么我们可以立刻发送abort命令给所有人,这个命令可能有两种结果:

    abort成功,那么整个事物回滚

    abort失败,意味着B机器超时,已经提交。那么A机器也只能提交。

    如果preCommit成功,那么为了加速提交过程,可以再发送一条commit命令给所有人,加速提交过程。

    以上的过程就是3pc的核心思路,各位看官可以根据这个思路,去推断和补充这套协议的其他内容,我就不再细说了,因为其他推论如果不是真正要去实现,意义不大,关键是核心思路。

    然后,我们来看看3pc的问题,其实,从我们讨论的过程中多次出现的东西,大家就能很容易的看出问题所在,他的假定实际上不是真实的双机或多机一致性场景,在这种场景里面,网络无响应也可能意味着网络隔离断开这种情况。但这种情况在3pc内是没有被考虑的。

    所以也因为这样,我们只是需要了解这东西的一个简单思路就行了,不过,所有的思路在你未来的生活中,都可能会成为你解决问题的利器,所以,我还是把这种模型的核心思路写出来。给大家做个参考。

    —–统一思想,做出统一决策———解决脑裂问题

    我们刚才也提到,网络上经常会因为不可抗力造成思维上的隔阂,比如A机房和B机房之间的主干路由器挂掉了啊等等情况。那么,在这种情况下,有什么办法能够在这种时候,通过一种机制来选择应该听哪一组集群的呢?

    问题在一致性问题的第一篇已经分析过了,所以我就不再重复了,只重复我们要做到的事情:”给定安全级别(美国被爆菊?火星人入侵?),保证服务可用性,并尽可能减少机器的消耗。”

    我们也给出了一种最简单的模型,观察者模式:

    海量存储之十七--一致性和高可用专题

    在这种模式中,A和B机房的网络如果断掉,只要他们到C机房的网络不同时断掉,那么就可以利用在机房C的观察者来协助判断谁是正确的。

    这样似乎问题不就解决了嘛?你或许会这样想?呵呵,那你就错过最好玩的东西了。。

    我们来看看,这是三台机器的情况,任意一台挂掉或者网络断掉,都可以保证结构不乱。

    那么,如果我有10台机器,有更多机器可能“挂掉”更多网段可能断开,这时候你会怎么安排机器的角色和网络结构呢?

    仔细想想,无论给你多少台机器,用observer模式,那么只要observer挂掉并且在{A}机房{B}机房的网络断开的情况下,系统会退化到脑裂问题上了。

    那么,简单的思路是,给Observer加机器不就好了?问题来了,Observer如果有两台,你到底应该听谁的?这些Observer部署到哪些机房?哪个Observer是真正管事儿的?比如如果有两个Observer, {Observer C和Observer D},各自为政,那么A机房正好问了Observer c,C说你是主机吧。然后B机房问了Observer D ,D说,你是主机吧。最后还是个脑裂的结局。。

    怎么样?再给你来几台机器?晕不晕?

    呵呵,我也不是难为大家,而是这些情况会实际的发生在一致性和高可用的整个过程里面。

    然后,我们能做的事情是什么?只好去找现实中的解决方法了。。

    让我们来吐槽一下Lamport.. 不得不说,这大神是个很好的数学家,但写的Paxos论文你妹怎么就这么晦涩呢?本来很简单的一个思路,让他描述一下我是看了很久没看懂啊- -。。最后还是得看in simple才勉强弄明白。。

    不过,paxos的论文文章其实也正披露了他思路的核心来源:

    信使就是消息传递,也就是通过网络将消息传递给其他人的机制。类似的机制还有人通过空气传递语言给其他人,作家通过文字将情感传递给读者,这些都是消息传递。所以也会有像我们李雷,韩梅梅关电灯一样的几种反馈。

    然后,Lamport大神还提到了一个重要的概念:议会。一看parliament,估计中国人就都晕了:这是嘛?我们伟大光荣正确的领路人的所有决议不都全票通过了么,也没讨论什么的。和paxos有神马关系?。

    其实这就是个悲哀了。。。在古希腊城邦时代,很多决议就是议会讨论并作出决议的。

    这种作出决议的方式,叫做“少数服从多数”。

    当然当然,你也可以说,不是说有多数派暴政么?不是说多数派更多的是《乌合之众》么?其实这也没办法的事情,所以丘吉尔才会说:“民主并不是什么好东西,但它是我们迄今为止所能找到的最不坏的一种。”

    我们就来看看,我们为什么需要少数服从多数这个原则

    我之后的讨论,纯粹的是在机器和机器之间做出决策所需,在实际的zz过程中,环境远比大家想想的复杂得多,变数很大,但作为计算机,所有的东西其实都是可预期的,所以我们还是回到计算机科学领域。

    假定有两个机房,A机房和B机房,A机房有5台机器,B机房有3台机器。他们的网络被物理隔离了。我们来看看我们有哪些选择:

    1.A,B机房独立的都可以提供服务。

    这种方式明显是不靠谱的,会出现不可逆转的不一致问题。咔嚓掉。

    2.A,B机房都不可以提供服务

    合理的方式,有些场景的时候会选择这种情况。但明显在高可用上面是0分,只保持了一致性而已。

    3.让A机房的机器服务。

    好吧。你真的认为剩下三台机器提供服务的安全性比5台的高?。。

    4.让B机房的机器服务。

    “民主并不是什么好东西,但它是我们迄今为止所能找到的最不坏的一种。”

    这就是qurom产生的核心原因。

    今天到这,后面我们会讨论paxos的一些细节,散会~

    我们已经在上面的分析中,我们已经看到observer模型在多机场景下的问题,所以,paxos模型的目标就是解决这个问题,他解决这个问题的方法就是quorum模型。

    我的目标是让大家能弄明白,掌握这些复杂的概念,所以我也会将以前我在淘宝java中间件团队内分享时候,大家经常犯的一些错误,也写到【】里面,尽可能让大家少走弯路,如果有什么感想,疑问,后面可以留言。

     

    PS 广告插播 : 淘宝java中间件团队,你值得拥有:)

     

    —-PAXOS——

    好,我们回顾一下上下文,我们在上篇文章中谈到,当机器变得更多的时候Observer不能只有一个。必须有更多个Observer,但Observer多了,到底听谁的又成了问题。你一言我一语,大家都觉得自己是老大,谁也不服谁。咋办捏?

    这时候就得有人站出来,说:那我们少数服从多数吧!制定一套策略,在各种情况下都能够选出一个决议不就行了!

    这其实就是paxos协议的核心想法之一,我们来看一下他是怎么做到的。在这里,我不想去做那个繁琐的证明过程,那个过程如果你感兴趣,可以去看paxos made simple这篇文章,有中文,这里给出http://blog.csdn.net/sparkliang/article/details/5740882 ,数星星同学也翻译过。可以直接google.

     

    我在这里只说结论,因为结论更容易理解一些。

    我们假定有A,B,C,D,E五台机器。kv系统需要put一个数据[key=Whisper -> val=3306]到我们这5台机器上,要保证只要反馈为真,任意两台机器挂掉都不会丢失数据,并且可以保证高可用。怎么做:

    1.首先,客户端随机选择一个节点,进行写入提交,这里我们随机选择了C这个节点,这时候C节点就是这次提议的发起人【也叫proposer,在老的2pc协议里也叫做coodinator】,当C收到这个提议的时候,C首先要做的事情是根据当前节点的最新全局global id,做一次自增操作,我们假定,在当时全局id,Global ID是0,所以,这个议案就被对应了一个编号,1—>[key=Whisper -> val=3306]。

    【【这里有两个我们经常犯的错误,下面做一个解说:

    1.global id问题,在老的论文里,Lamport没有描述这个自增id是怎么生成的,所以大家的第一个疑问一般是问id怎么生成,从我目前能够看到的所有实现里面,基本上就是选择哪一台机器,就是以那台机器当前所保持的全局id(snapshot,可能不是全局来看的最高值,但没关系,只要是自己这台机器的最高值就行了),然后做一下自增就行了。我们后面会看到协议如何保证非全局最高值的globalID提议会被拒绝以至于不能够形成决议。

    2.global id —>[key=Whisper -> val=3306] . 这也是个会让人困惑的问题,在原文中,他被表示为一个key-value的形式,比如proposal[0->value] 。这会让人自然的联想到与数据库的kv相对应,key是0,value是value。然后就会困惑,这个数据是怎么和数据库对应起来的呢?这是我当时的困惑,现在也把他列在这里。其实很简单,这里的global id对应value.global id只是对paxos协议有意义,对于数据库,其实只需要关心value里面的数据即可,也即将global id —>[key=Whisper -> val=3306]里面的value: [key=Whisper-> val=3306] 作为数据库构建映射时所需要的redoLog就行了,global id的作用只是告诉你这些数据的顺序是按照global id来排列的,其他无意义。    】】

     

    我们回到文中,我们已经将这个新的议案标记了从C这台机器看起来最大的global id : 1—>[key=Whisper -> val=3306]。然后,他会尝试将这个信息发送给其余的A,B,D,E这几台机器。

    我们来看这些机器的操作流程。 在这个过程中,Paxos将A,B,D,E叫做accepter【老的协议里没有区分,管这些都叫做参与者,cohorts】,他们的行为模式如下:

    如果A,B,D,E这几台机器的globalID 小于C给出的决议的GID(1—>[key=Whisper -> val=3306]),那么就告诉C,这个决议被批准了。而如果A,B,D,E这几台机器的GlobalID 大于或等于C给出决议的GID.那么就告知C 这个决议不能够被批准。

    我们假定A,B两台机器当时的Max(GID)是0 ,而D,E的Max(GID)是1.那么,A,B两台机器会反馈给C说协议被接受,这时候我们算算,C的议案有几票了?A+B+!C!,一定要算自己哦:) 。所以,这个议案有三票,5台机器的半数是3.超过法定人数,于是决议就被同意了。

     

     

    我们保持这个上下文,来看看D,E这边的情况。首先,要思考的问题是,为什么D,E的Max(GID)是1呢?

    其实很简单,D可能在C发起决议的同时,也发起了一个决议,我们假定这个决议是由D发起的,决议是 1—>[key=taobao ->val=1234]。既然D,E的Max(GID)是1,那么意味着E已经告知D,它同意了他的决议,但D马上会发现,A,B,C里面的任意一个都返回了D不同意。他的议案只拿到两票,没有通过,它虽然有点不爽,但也是没办法的事情啊。。

     

    这时候C的决议已经被多数派接受,所以他需要告知所有人,我的议案1—>[key=Whisper -> val=3306]已经被接受,你们去学习吧。

     

     

     

    这时候还有一个问题是需要被考虑的,如果在C已经得知决议已经达到法定人数,在告知所有人接受之前,C挂了,应该怎么办呢?

    我之所以没有将这个放到开始的描述里,主要原因是觉得这是个独立因素,不应该影响议案被接受时候的清晰度。

    为了解决这个问题,需要要求所有的accepter在接受某个人提出的议案之后,额外的记录一个信息:当前accepter接受了哪个提议者的议案。

     

    为什么要记录这个?很简单,我们看一下上面出现这个情况时候的判断标准。

    A 机器:角色-accepter 。 批准的议案 1—>[key=Whisper-> val=3306] 。提议人:C

    B 机器:角色-accepter 。 批准的议案 1—>[key=Whisper-> val=3306] 。提议人:C

    C机器:角色-proposer 。 挂了。。不知道他的情况。

    D 机器:角色-accepter 。 批准的议案 1—>[key=taobao->val=1234] 。提议人:自己

    E 机器:角色-proposer。 “提议的”议案 1—>[key=taobao->val=1234] 。提议人:D。

     

    因为有了提议人这个记录,所以在超时后很容易可以判断,议案1—>[key=Whisper -> val=3306] 是取得了多数派的议案,因为虽然D,E两台机器也是可以达成一致的议案的。但因为有个人本身是提议者,所以可以算出这个议案是少数派。

    就可以知道哪一个议案应该是被接受的了。

     

     

     

    在这之后,提议者还需要做一件事,就是告知D,E,被决定的决议已经是什么了。即可。

    这个过程在文章中叫Learn. D,E被称为Learner.

    别看写的简单,这个过程也是变数最大的过程,有不少方法可以减少网络传输的量,不过不在这里讨论了。

     

     

     

     

    下面,我们讨论一下我们在2pc/3pc中面临的问题,在paxos里面是怎么被解决的。

    2pc最主要的问题是脑裂,死等。两个问题。

    对于脑裂,paxos给出的解决方案是,少数服从多数,决议发给所有人,尽一切努力送达,总有一个决议会得到多数派肯定,所以,不在纠结于某一台机器的反馈,网络无响应?没有就没有吧,其他人有反馈就行了。

    所以,如果出现了机房隔离的情况,比如A,B,C在机房1,D,E在机房2,机房1和机房2物理隔离了,那么你会发现,D,E永远也不可能提出能够得到多数派同意的提案。

    所以,少数派的利益被牺牲了。。换来了多数派的可用性。我们分析过,这是唯一能够既保证数据的一致性,又尽可能提高可用性的唯一方法。

    而对于死等问题,解决的方法也是一样的,对于某一台机器的无响应,完全不用去管,其他机器有相应就行了,只要能拿到多数,就不怕一小撮别有用心的反对派的反攻倒算~。

     

     

     

    ———————————paxos就是这样一个协议———-

    休息一下

    —————————————————————————————-

     

     

     

     

    那么Paxos有没有什么值得改进的地方?有的,很简单,你会发现,如果在一个决议提议的过程中,其他决议会被否决,否决本身意味着更多的网络io,意味着更多的冲突,这些冲突都是需要额外的开销的,代价很大很大。

    为了解决类似的问题,所以才会有zoo keeper对paxos协议的改进。zk的协议叫zab协议,你可以说zab协议不是paxos,但又可以说是paxos.但将paxos和zab协议之间做直接的等同关系,无疑是【错误】的。

     

    其实,这也是在我们的现实生活中经常能够发现的,如果每个议案都要经过议会的讨论和表决,那么这个国家的决策无疑是低效的,怎么解决这个问题呢?弄个总统就行了。zab协议就是本着这个思路来改进paxos协议的。

     

     

    ———paxos 改进—-zab协议讨论—————–

    zab协议把整个过程分为两个部分,第一个部分叫选总统,第二个部分叫进行决议。

    选总统的过程比较特殊,这种模式,相对的给人感觉思路来源于lamport的面包房算法,这个我们后面讲。,选择的主要依据是:

    1.如果有gid最大的机器,那么他是主机。

    2.如果好几台主机的gid相同,那么按照序号选择最小的那个。

    所以,在开始的时候,给A,B,C,D,E进行编号,0,1,2,3,4。 第一轮的时候,因为大家的Max(gid)都是0,所以自然而然按照第二个规则,选择A作为主机。

    然后,所有人都知道A是主机以后,无论谁收到的请求,都直接转发给A,由A机器去做后续的分发,这个分发的过程,我们叫进行决议。

    进行决议的规则就简单很多了,对其他机器进行3pc 提交,但与3pc不同的是,因为是群发议案给所有其他机器,所以一个机器无反馈对大局是没有影响的,只有当在一段时间以后,超过半数没有反馈,才是有问题的时候,这时候要做的事情是,重新选择总统。

    具体过程是,A会将决议precommit给B,C,D,E。然后等待,当B,C,D,E里面的任意两个返回收到后,就可以进行doCommit().否则进行doAbort().

    为什么要任意两个?原因其实也是一样的,为了防止脑裂,原则上只能大于半数,不能少于半数,因为一旦决议成立的投票数少于半数,那么就存在另立中央的可能,两个总统可不是闹着玩的。

    定两个,就能够保证,任意“两台”机器挂掉,数据不丢:),能够做到quorum。。

     

    然后是我的个人评述,写zab协议的人否认自己的协议是paxos.变种 其实我也是有些认同的。不过,他们是针对一个问题的两种解决方法:

    因为他们解决的问题的领域相同

    解决网络传输无响应这个问题的方法也一样:也即不在乎一城一池的得失,尽一切努力传递给其他人,然后用少数服从多数的方式,要求网络隔离或自己挂掉的机器,在恢复可用以后,从其他主机那里学习和领会先进经验。

    并且也都使用了quorum方式来防止脑裂的情况。

    核心思路是类似的,但解决问题的方法完全是两套。 paxos在其他公司的实现里面也对paxos进行了这样,那样的改进。不过核心思路都是这个。

     

    我们对paxos协议的讲解,就到这里。

     

    也留下一个问题,zab协议,如果我们用在google 全球数据库spanner上,会不会有什么问题呢?请大家思考哈 :)

     

    后记,抱歉,这篇文章一个图都没有。。我已经尽可能用简单的方式来描述paxos和他的变种协议了(当然有一个作为了问题)。如果有哪个地方不明白,也还请在后面留言吧。友情提示这篇文章不适于跳跃性阅读,想要理解,必须从第一行开始读到最后。。。。

     

    google的工程师说,所有的一致性协议都是paxos的特例,我表示不置可否吧。。。。下一篇我们要讨论另外一系的实现,gossip模型的实现。我个人感觉:把gossip归类到paxos模型,似乎也不是很合适。gossip协议的两个主要的实现方式,是dynamo和cassandra.我们在下一篇里面进行讨论


  • 相关阅读:
    补习系列(4)-springboot 参数校验详解
    华为鲁勇:5G+云+AI三大核心引擎将驱动广州数字经济发展
    selenium firefox
    徐翔之后新生代“敢死队”浮出水面:八年一万倍
    ActiveMQ讯息传送机制以及ACK机制
    一元线性回归分析及java实现
    jsoup -- xml文档解析
    HDU 1020 Encoding 字符串
    PHP缓存之文件缓存
    Eclipse PHPEclipse 配置
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959483.html
Copyright © 2011-2022 走看看