Paxos算法

一、基本要求

1、拜占庭将军问题

    拜占庭帝国有许多支军队,不同的将军之间必须制定一个统一的行动计划,从而做出进攻或者撤退的决定。同时,各个将军在地理上都是被分隔开来的,只能依靠军队的通讯员来进行通讯。然而,在所有的通讯员中可能存在叛徒,这些叛徒可以任意篡改消息,从而达到欺骗将军的目的。

2、要求

    假设有一组可以提出提案的进程集合,那么对于一致性算法来说,需要保证以下几点:

  1. 这些被提出的提案中,只有一个会被选定;
  2. 如果没有提案被提出,那么就不会有被选定的提案;
  3. 当一个提案被选定后,进程可以获取被选定的提案的信息。

    分布式算法有两个重要的属性:安全性和活性。安全性是指需要保证永远都不会发生的事情,活性是最终一定会发生的事情。这里不精确的去定义活性的需求,只讨论安全性的要求:

  1. 只有被提出的提案才能被选定;
  2. 只有一个值能被选定;
  3. 如果某个进程认为某个提案被选定了,那么这个提案必须真的是被选定的。

3、角色

    在Paxos算法中,涉及到三种角色:Proposer、Acceptor和Learner。Proposer是提出提案的角色,Acceptor是判断是否接受提案的角色。在算法实现的过程中,不同的进程可能充当不同的角色。假设不同的参与者之间可以通过收发消息来进行通信,那么:

  1. 每个参与者以任意的速度执行,可能会因为出错而停止,也可能会重启。同时,即使一个提案被选定后,所有的参与者也有可能会失败或重启,因此除非失败或重启的参与者可以记录一些信息,否则将无法确定最终的值;
  2. 消息在传播的过程中可能会出现不可预知的延迟,也可能会重复或丢失,但是消息不会被损毁,即消息内容不会被篡改(即拜占庭将军问题)。

二、算法详解

1、提案选定相关的两个假设

    首先,确定一个基本概念,一个提案指的是[Mn, Vn],其中Mn是一个全局唯一的编号,而Vn是提案的值,即需要提交的内容本身。

    P1:一个Acceptor只要未响应过任何编号大于Mn的Prepare请求,那么它就可以接受这个编号为Mn的提案;

    P2:对于产生的每个提案,需要满足:存在一个超过半数的Acceptor组成的集合S,要么S中没有Acceptor批准过编号小于Mn的任何提案,要么S中所有的Acceptor批准的所有编号小于Mn的提案中,编号最大的那个提案的Value值为Vn。

2、提案的生成

    根据上面的假设P2进行提案的生成。Proposer在产生一个编号为Mn的提案时,必须要知道某一个将要或者已经被半数以上的Acceptor批准的编号小于Mn的最大的编号的提案,并且Proposer会要求Acceptor不再批准任何编号小于Mn的提案。

2.1 提案生成算法

    根据上面的要求,得到提案生成算法:

1、Proposer选择一个新的提案编号Mn,然后向某个Acceptor集合的成员发送请求,要求该集合中的Acceptor做出如下回应:

  • 向Proposer承诺,保证不再批准任何编号小于Mn的提案;
  • 如果Acceptor已经批准过任何提案,就想Proposer反馈该提案的值。

    这个请求被称为:编号为Mn的提案的Prepare请求。

2、根据Proposer收到的响应结果不同,有如下不同的Vn的处理方式:

  • 如果Proposer收到了来自半数以上的Acceptor的响应结果,那么它就可以产生编号为Mn、value为Vn的提案,这里的Vn是所有响应中编号最大的提案的Value值;
  • 如果Proposer收到的响应结果是半数以上的Acceptor都没有批准过任何提案,即响应中不包含任何的天,那么此时Vn的值可以由Proposer任意选择。

2.2 Accept请求

    在确定提案之后,Proposer会将提案再次发送给某个Acceptor集合,并期望获得它们的批准,这个请求被称为Accept请求。需要注意的是,此时接受Accept请求的Acceptor集合不一定是之前响应Prepare请求的Acceptor集合,但是任意两个半数以上的Acceptor集合,必定包含至少一个公共Acceptor。

3、提案的批准

    可以得到两阶段提交算法:

阶段一:

  1. Proposer选择一个提案编号Mn,然后向Acceptor的某个超过半数的子集成员发送编号为Mn的Prepare请求;
  2. 如果一个Acceptor收到一个编号为Mn的Prepare请求,且编号Mn大于该Acceptor已经响应的所有Prepare请求的编号,那么它就会将已经批准过的最大编号的提案作为响应反馈给Proposer,同时该Acceptor会承诺不会再批准任何编号小于Mn的提案。

阶段2:

  1. 如果Proposer收到来自半数以上的Acceptor对于其发出的编号为Mn的Prepare请求的响应,那么它就会发送一个针对[Mn, Vn]提案的Accept请求给Acceptor。需要注意的是,Vn的值就是收到的响应中编号最大的提案的值,如果响应中不包含任何提案,那么它就是任意值;
  2. 如果Acceptor收到这个针对[Mn, Vn]提案的Accept请求,只要该Acceptor尚未对编号大于Mn的Prepare请求作出响应,它就可以通过这个提案。

4、提案的获取

    上述三节已经讨论了如何选定一个提案,这里讨论如何让Learner获取提案。

    所有的Acceptor将它们对提案的审批情况,统一发送给一个特定的Learner集合,该集合中的每个Learner都可以在一个提案被选定后通知所有其他的Learner这个Learner集合中的Learner个数越多,可靠性就越好,但同时网络通信的复杂度也就越高。

5、选取主Proposer

    Proposer如果发现自己提议的编号过小导致被Acceptor拒绝,会获取新的更大的编号并重发提议,这就可能导致出现两个Proposer循环交替的不断提出两个提议的死循环。可以通过选定一个主Proposer,并规定只有主Proposer才能提出提议来避免这个问题。只要主Proposer和过半的Acceptor能进行正常的网络通信,那么但凡Proposer提出一个编号更高的提议都会被批准。

    通过选定一个主Proposer,可以保证算法的活性。

发布了95 篇原创文章 · 获赞 15 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章