1. Paxos算法

  Paxos算法是Lamport提出的一种基于消息传递的分布式一致性算法,开源的Zookeeper、MySQL5.7推出的取代传统主从复制的MySQL Group Replication等纷纷采用Paxos算法解决分布式一致性问题;

2. Paxos算法流程

  Paxos算法运行在允许宕机故障的异步系统中,不要求可靠的消息传递,可以容忍消息丢失、延迟、乱序以及重复;利用大多数(Majority)机制保证2F+1的容错能力,即2F+1个节点的系统最多允许F个节点同时出现故障;

  一个或多个提议进程(Proposer)可以发起提案(Proposal),Paxos算法使所有提案中的某一个提案,在所有进程中达成一致;当系统中的多数派认同该提案时,即达成一致;最终只会针对一个确定的提案达成一致;

  Paxos将系统中的角色分为提议者Proposer、决策者Acceptor、最终决策学习者Learner;

  Proposer:提出提案Proposal,提案中的信息包括提案编号和提议的值;

  Acceptor:参与决策,回应Proposers的提案;收到Proposal后,可以选择接受提案,若Proposal获得多数Acceptors的接受,则称该Proposal被批准;

  Learner:不参与决策,从Proposers/Acceptors学习最新达成一致的提案;

  多副本状态机中,每个副本会同时具有Proposer、Acceptor、Learner角色;

3. Paxos算法决议流程

  1). Prepare阶段;Proposer向Acceptor发出Prepare请求,Acceptors针对收到的Prepare请求进行Promise承诺;

  2). Accept阶段:当Proposer收到多数Acceptors承诺的Promise后,向Acceptors发出Propose请求,Acceptors针对收到的Propose请求进行Accept处理;

  3). Learn阶段:Proposer在收到多数Acceptors的Accept之后,标志着此次Accept成果,决议形成,将形成的决议发送给所有Learners;

4. Paxos算法流程中的每条消息

  1). Prepare:Proposer生成全局唯一且递增的Proposal ID,向所有Acceptors发送Prepare请求,此时无需携带提案内容,只需要携带Proposal ID即可;

  2). Promise:Acceptors收到Prepare请求后,做出"两个承诺、一个应答";

  两个承诺:

  - 不在接受Proposal ID小于等于当前请求的Prepare请求;

  - 不在接受Proposal ID小于当前请求的Propose请求;

  一个应答:

  - 在不违背以前做出承诺的前提下,回复已经Accept过的提案中Proposal ID最大的那个提案的Value和Proposal ID,没有则返回空值;

  3). Propose:Proposer收到多数Acceptors的Promise应答后,从应答中选择Proposal ID最大的提案value值,作为本次要发起的提案;如果所有应答的提案value均为空值,则可以自己随意决定提案Value;然后携带当前Proposal ID,向所有Acceptors发送Propose请求;

  4). Accept:Acceptor收到Propose请求后,在不违背自己做出的承诺下,接受并持久化当前Proposal ID和提案;

  5). Learn:Proposer收到多数Acceptors的Accept后,决议形成,将形成的决议发送给所有Learners;

5. 问题推导

  1). 当只有一个Acceptor:

  假设只有一个Acceptor,只要Acceptor接受收到的第一个提案,则提案被选定,提案的Value就是被选定的value,如此,则保证只有一个值会被选定;但是,如果唯一的Acceptor宕机,则整个系统无法工作;因此,整个系统必须存在多个Acceptor;

  P1:一个Acceptor必须接受它收到的第一个提案;

  2). 多个Acceptor:

  当多个Proposal向Acceptor发送提案,Acceptor接受自己收到的value,会导致不同的value被选定,出现不一致,因此需要增加规定:一个提案被选定需要被半数以上的Acceptor接受;于是增加以下的约束:

  P2a: 如果某个value值为v的提案被选定,则编号更高的被Acceptor接受的提案的value必须也是v;

  但是,考虑如下的情况:假设总共有5个Acceptor。Proposer2提出[M1,V1]的提案,Acceptor25(半数以上)均接受了该提案,于是对于Acceptor25和Proposer2来讲,它们都认为V1被选定。Acceptor1刚刚从宕机状态恢复过来(之前Acceptor1没有收到过任何提案),此时Proposer1向Acceptor1发送了[M2,V2]的提案(V2≠V1且M2>M1),对于Acceptor1来讲,这是它收到的第一个提案。根据P1(一个Acceptor必须接受它收到的第一个提案。),Acceptor1必须接受该提案!同时Acceptor1认为V2被选定。这就出现了问题:

  Acceptor1认为V2被选定,Acceptor2~5和Proposer2认为V1被选定。出现了不一致。V1被选定了,但是编号更高的被Acceptor1接受的提案[M2,V2]的value为V2,且V2≠V1。这就跟P2a(如果某个value为v的提案被选定了,那么每个编号更高的被Acceptor接受的提案的value必须也是v)矛盾了。于是,增加新的约束:

  P2b:如果某个value为v的提案被选定,那么之后任何Proposal提出的编号更高的提案的value值也必须是v;

  P2c:对于任意的N和V,如果提案[N,V]被提出,那么存在一个半数以上的Acceptor组成的集合S,满足以下两个条件中的任意一个:

  - S中每个Acceptor都没有接受过编号小于N的提案;

  - S中Acceptor接受过的最大编号的提案的value为V;

6. Proposal生成提案

  为了满足P2b,提出一个比较重要的思想:Proposal生成提案之前,应该先去学习已经被选定或可能被选定的value,然后以该value作为提出提案的value值,如果没有value被选定,Proposal才可以自己决定value的值。这个学习阶段是通过一个Prepare请求实现的;

  提案生成算法:

  1). Proposal选择一个新的提案编号N,然后向某个Acceptor集合发送请求,要求该集合中的每个Acceptor做出如下响应:

  - 向Proposal承诺保证不再接受任何编号小于N的提案;

  - 如果Acceptor已经接受过提案,则向Proposal响应已经接受过的编号小于N的最大编号的提案;

  我们将该请求称为编号为N的Prepare请求;

  2). 如果Proposal收到半数以上的Acceptor的响应,则它就可以生成编号为N,Value为V的提案[N,V]。V是指所有的响应中编号最大的提案的value;如果所有的响应中都没有提案,则此时V可以由Proposal自己选择。

  3). 生成提案后,Proposal将该提案发送给半数以上的Acceptor集合,并期望这些Acceptor能接受该提案,我们称该请求为Accept请求;(此时接受Accept请求的Acceptor集合,不一定是之前响应Prepare请求的Acceptor集合)

7. Acceptor接受提案

  Acceptor可以忽略任何请求,而不用担心破坏算法的安全性。我们对Acceptor接受提案给出如下约束:

  P1a:一个Acceptor只要尚未响应过任何编号大于N的Prepare请求,那么它就可以接受编号为N的提案;如果Acceptor收到一个编号为N的Prepare请求,在此之前它已经响应过编号大于N的Prepare请求;根据P1a,该Acceptor不可能接受编号为N的提案。因此,该Acceptor可以忽略编号为N的Prepare请求。也可以回复一个error,让Proposal尽早知道自己的提案不会被接受;

  一个Acceptor只需要记住:

  - 已接受的编号最大的提案;

  - 已响应的请求最大的编号;

8. Paxo算法完整阶段:

  Paxos算法分为两个阶段:

  阶段一:

  (a)提案编号N,然后向半数以上的Acceptor发送编号为N的Prepare请求

 

  (b) 如果一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的所有Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给Proposer,同时该Acceptor承诺不再接受任何编号小于N的提案

 

  阶段二:

 

  (a) 如果Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案Accept请求半数以上的Acceptor。注意:V就是收到的响应编号最大的提案的value,如果响应中不包含任何提案,那么V就由Proposer自己决定

 

  (b) 如果Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于NPrepare请求做出过响应,它就接受该提案

9. Learner学习的三种方案

  1). Acceptor接受一个提案,就将该提案发送给所有Learner;

  2). Acceptor接受一个提案,就将该提案发送给主Learner,主Learner再通知其他Learner;

  3). Acceptor接受一个提案,就将该提案发送给一个Learner集合,Learner集合再通知其他Learner;

10. 如何保证Paxos算法的活性

  假设有两个Proposer依次提出编号递增的提案,最终会陷入死循环,没有value被选定;只有通过选取主Proposer,可以保证算法的活性,只有主Proposer才能提出提案;

  通过选取主Proposer,就可以保证Paxos算法的活性。至此,我们得到一个既能保证安全性,又能保证活性分布式一致性算法——Paxos算法

  

  

 

  

posted on 2024-04-18 15:12  VaeSSAQ  阅读(10)  评论(0)    收藏  举报