从Paxos到ZooKeeper-一致性协议之Paxos算法

一、简介

    在上一节的ZooKeeper-一致性协议之2PC、3PC中对二阶段提交和三阶段提交协议进行了解析,而这节主要是Paxos算法的讲解

    Paxos算法是Leslie Lamport在1990年提出的一种基于消息传递、具有高度容错性的一致性算法,是目前解决分布式一致性问题的有效算法。

    Lamport在其论文中提出了这样一个场景来描述Paxos一致性算法需要解决的问题:在古希腊有一个Paxos小岛,岛上以议会的形式通过法令。议会中的议员通过信使传递消息,议员和信使都是兼职的,随时可能离开议会厅,并且信使可能重复投递消息,也可能一去不复返。议会协议要保证在这种情况下法令仍然能够正确的产生,并且不会出现冲突

    在常见的分布式系统中,总会发生机器宕机或僵死、网络异常等问题,而Paxos算法解决的问题就是在可能发生以上异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,保证无论发生任何以上任何异常都不会破坏整个系统的一致性(对比来看,信使是网络,议员是分布式节点,法令则是某个数据的值)

    从理论上讲,分布式计算领域中,如果在异步系统和不可靠的通道上来达成一致是不可能的,所以对一致性的研究都是基于可靠信道即假设所有消息都是完整的,没有被篡改

二、Paxos算法详解

1. 问题描述

    分布式算法有两个主要的属性:

安全性:需要保证永远都不会发生的事情
活性:最终一定会发生的事情

    对于一致性协议来说,其安全性需求为:

只有被提出的提案才能被选定
只能由一个提案被选定
当进程认为提案被选定后,该提案必须是真的被选定的那个

    Paxos算法的目标就是保证最终只会有一个提案被选定,并且当提案选定后,进程最终也能获取到被选定的提案

    在整个一致性算法中,有三个参与角色,即Proposer、Acceptor、Learner。各个参与者可以收发消息进行通信,但以任意速度执行也可能会因为出错而停止或重启;整个消息在传输过程中可能会出现不可预知的延迟、丢失或重复,但其内容不会被篡改

2. 提案选定

    选定一个提案最简单的方式即为只有一个Acceptor,所有的Proposer都将提案发送给Acceptor进行选择,但这种方式很明显出现单点问题

    为了解决这个问题,可以使用多个Acceptor。也就是说Proposer向一个Acceptor集合发送提案,当有足够多(超过一半)的Acceptor批准该提案时就认为该提案被选定。

** 为什么要超过一半:任意两个包含大多数(超过一半)Acceptor子集至少有一个公共成员

3. 推导过程

  • 需求 1:Acceptor必须批准它收到的第一个提案 –> 在没有失败和消息丢失的情况下,即使只有一个提案被提出仍然可以选出一个提案

    可是这样会导致另一个问题,即没有一个提案是多数人都批准的

这里写图片描述

    在需求一的基础上加上一个提案需要半数以上Acceptor批准 –> Acceptor能够批准不止一个提案。

    在Paxos中引入了一个全局的编号来唯一标识每个被Acceptor批准的提案,当一个具有某Value值的提案被本数以上Acceptor批准后,就认为该Value已被选定,也就是说现在提案表示为[编号,Value]。

    虽然我们允许多个提案被选定,但同时必须要保证所有被选定的提案都具有相同的Value值

  • 需求 2:当提案[M0,V0]被选定后,所有编号比M0高且被选定的提案,其Value值必须为V0

    因为提案的编号有序,需求2就保证了只有一个Value值被选定这一安全性需求;并且当一个提案被选定表明其被至少一个Acceptor批准过

  • 需求 2.1:当提案[M0,V0]被选定后,所有编号比M0高,且被Acceptor批准的提案,其Value值必须为V0

    这个时候,我们仍需要需求 1来保证提案会被选定,但是也可能会出现Acceptor在选定提案后收到新的提案,如下图,此时提案[M0,V0]已被选定,M1大于M0,V1不等于V0,而根据需求1来看,Acceptor必须批准该提案,如此一来,就与需求 2.1相悖

这里写图片描述

  • 需求 2.2:当提案[M0,V0]被选定后,任何Proposer产生的编号更高的提案,其Value值都为V0 -> 强化需求2.1,解决相悖问题

    提案必须在被Proposer提出后才能被Acceptor批准,所以需求2.2包含了需求2.1包含了需求2,所以接下来证明需求2.2即可

4. 数学归纳法证明

    假设编号在M0到Mn-1的提案的Value值都是V0,证明编号Mn的提案的Value值也是V0

    当编号为M0的提案已经被选定,则表明肯定存在由半数以上Acceptor组成的集合C,C中的每个Acceptor都批准了一个编号在M0到Mn-1的提案,其值为V0,因为任何包含半数以上的Acceptor集合S都包含了至少一个C中的成员,则可以提出新的需求

  • 需求 2.3:对于任何的Mn和Vn,如果提案[Mn,Vn]被提出,则肯定存在一个由半数以上Acceptor组成的集合S,满足以下条件:S中不存在任何批准过的编号比Mn小的提案的Acceptor;所有Acceptor批准过的编号小于Mn的提案中编号最大的那个提案的值为Vn

    第一个条件即是说没有提案被选定;第二个条件即是说当提案被选定后,任何Proposer产生编号更高的提案的值都为被选定的提案值

    这样只要满足需求2.3就可以满足需求2.2

    首先假设提案[M0,V0]被选定了,设比该提案编号大的提案为[Mn,Vn],证明在需求2.3的前提下,对于所有[Mn,Vn],存在V0=Vn

(1)当Mn=M0+1时

    肯定存在超过半数的Acceptor子集S批准了小于Mn的提案,此时Vn只能是S中编号小于Mn的拥有最大编号的提案的值,又因为Mn=M0+1,所以拥有小于Mn的最大编号的提案即为[M0,V0],所以Vn的值为V0

(2)根据假设编号在M0+1到Mn-1区间的所有提案的Value值为V0,证明M的提案的Value值也为V0

    同样根据需求2.3,肯定存在超过半数的Acceptor子集S并且批准了小于Mn的提案,则Mn的Value值只能是S中编号小于M但是拥有最大编号的提案的值,如果是在M0+1到Mn-1区间,则Value值为V0;如果不在M0+1到Mn-1区间,编号也不可能比M0再小了(S和批准[M0,V0]提案的Acceptor多数集有交集),也就是M0,所以值也是V0

5. Proposer生成提案算法

    Proposer在生成一个编号为Mn的提案时,必须要知道当前某个将要或已经被半数以上Acceptor批准的编号小于n但是拥有最大编号的提案,并且Proposer要求所有的Acceptor都不要批准任何编号小于Mn的提案

(1)Prepare请求

    Proposer选择新的提案编号Mn,向某个Acceptor集合成员发送请求,并要求该集合中的Acceptor做出回应:保证不再批准任何编号小于Mn的提案;如果Acceptor已经批准过任何提案,则返回该Acceptor批准过的编号小于Mn但是拥有最大编号的提案的值

(2)确认Value值

    当Proposer接收到半数以上Acceptor响应结果后就可以产生[Mn,Vn]提案,这里的Value值为所有响应中编号最大的提案的Value值,如果没有选定的提案,此时Value值可由Proposer任意指定



  当确定提案后,Proposer会再次将该提案发送给某个Acceptor集合并期望获得其批准,这里的请求称之为Accept请求(* Accept请求接收的Acceptor集合和之前Prepare请求接受的Acceptor集合不一定是同一个)

6. Acceptor批准提案算法

    一个Acceptor只要尚未响应过编号大于Mn的Prepare请求,就可以接受这个编号为Mn的提案

7. 算法优化

    前面提到说Acceptor回应Prepare请求时保证不再批准任何编号小于Mn的提案,这时,当出现小于Mn的提案时可以选择忽略这样的Prepare请求,同时,也可以忽略掉那些它已经批准过的提案的Prepare请求,也就是说Acceptor只需要记住它已经批准过的提案的最大编号以及做出Prepare请求响应的提案的最大编号(响应Prepare请求后Proposer的Accept请求并不一定选到了该Acceptor)

三、算法重声

1. 阶段一

Proposer选择提案编号并向Acceptor的某个超过半数的子集成员发送Prepare请求

Acceptor收到编号为Mn的Prepare请求,并且编号Mn大于该Acceptor已经响应的所有Prepare请求的编号,则将它已经批准过的最大编号的提案作为响应反馈给Proposer并承诺不再批准编号小于Mn的提案

2.阶段二

Proposer接收到来自半数以上Acceptor的响应后,会发送一个[Mn,Vn]提案的Accept请求给Acceptor子集(Vn为所有响应中编号最大的提案的值)
Acceptor收到Accept请求后,只要该Acceptor尚未对编号大于Mn的Prepare请求做响应,就可以通过该提案

    实际上,每个Proposer都可以在任意时刻丢弃一个提案。同样如果Acceptor因为收到过更大编号的Prepare请求而忽略了编号更小的Prepare请求或Accept请求,它也应当通知对应的Proposer以便该Proposer也能够丢弃该提案

四、提案获取

    上面当选定了一个提案后,Learner获取一个提案的方案有如下几种

1. Acceptor推送

    Learner获取一个被选定的提案的前提是该提案已经被超过半数的Acceptor批准,所以最简单的做法即为一旦Acceptor批准了一个提案,就将该提案发送给所有Learner

    每个Acceptor与所有Learner通信,次数至少为两者个数乘积

2. 主Learner

    假定Learner之间可以进行消息通信,一旦Acceptor批准了一个提案后,统一发送给一个特定的Learner,再由主Learner将其同步给其他Learner

    通信次数为Acceptor和Learner个数总和,但主Learner随时可能出现故障

3. 主Learner集合

    方案2中最大问题即为主Learner的单点问题,可以将主Learner范围扩大为Learner集合,集合中每个Learner都可以在提案被选定后通知所有其他的Learner

    集合个数越多,可靠性越强,但网络通信次数越高

五、活性保持

    在实际运行中,Paxos算法还有这样一个细节,即一种极端情况:两个Proposer依次提出了系列编号递增的提案,但最终都没法被选定

    P1提出了编号M1提案,完成了阶段1的流程,同时P2也提出了M2的提案,完成了阶段1的流程,当P1进入阶段2时Acceptor无法批准编号小于P2的提案,所以P1再次进入阶段1提出了M3的提案导致P2进入阶段2时的Accept请求被忽略… …由此进入一个死循环

    为了避免上述问题,必须从Proposer中选择一个主Proposer,规定只有主Proposer才能提出议案,如此只要主Proposer能够和过半的Acceptor正常通信,只要主Proposer提出编号更高的提案就会被批准

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值