Distributed | Paxos
自Paxos问世以来就持续垄断了分布式一致性算法,Paxos这个名词几乎等同于分布式一致性。Google的很多大型分布式系统都采用了Paxos算法来解决分布式一致性问题。在学习了Raft算法之后自然不能错过。
[论文地址]
1. 相关概念
在Paxos算法中,有三种角色:
- Proposer
- Acceptor
- Learners
一个节点可以同时充当多个角色,既是 Proposer ,又是 Acceptor,又是 Learners
2.算法背景
Paxos算法是解决分布式一致性问题的共识算法,使得分布式系统中的各个进程就某个决议达成一致。
Paxos算法运行在允许宕机故障的异步系统中,不要求可靠的消息传递,可容忍消息丢失、延迟、乱序以及重复。但是信息不能被篡改,即属于非拜占庭模型。它利用大多数 (Majority) 机制保证了2F+1的容错能力,即2F+1个节点的系统最多允许F个节点同时出现故障。
- Proposer:提出proposal。Proposal信息包括提案编号(Proposal ID)和提议的值(Value)。
- Acceptor:参与决策,回应Proposers的提案。收到Proposal后可以接受提案,若Proposal获得多数Acceptors的接受,则该Proposal被选定。
- Learner:不参与决策,从Proposers/Acceptors学习最新达成一致的提案(Value)。
推导定理
在论文中通过一系列的推导,引出Paxos的三条规定:
- 当一个Acceptor没有响应任何编号大于N的Prepare请求,那么他就可以接受这个编号为N的提案。
- 一个提案被选定需要被半数以上的Acceptor接受才能被选定
- 如果某个value为v的提案被选定了,那么之后任何Proposer提出的编号更高的提案的value必须也是v。
其中1、2很好满足,而第3条需要通过另一个规定进行实现:
- 对于任意的N和V,如果提案[N,V]被提出,那么存在一个半数以上的Acceptor组成的集合S,满足以下两个条件中的任意一个:
- S中每个Acceptor都没有接受过编号小于N的提案
- S中Acceptor接受过的最大编号的提案的value 为 V。
提案生成算法
基于之前的第四条规定,Proposer生成提案之前,应该先去学习之前的value,避免不一致的提案产生。
因此提案生产可以分为两个步骤:
-
Prepare阶段:
Proposer生成一个递增的提案编号N,然后向Acceptor发送请求,要求每个Acceptor做出以下响应:
(1)向Proposer承诺保证不再接受任何编号小于N的提案。
(2)如果Acceptor中存在小于N的提案,就向Proposer响应已经接受过的编号小于N的最大编号的提案。
-
Accept阶段:
如果Proposer收到了半数以上的Acceptor的响应,那么它就可以生成编号为N,Value为V的提案[N,V]。这里的V是所有的响应中编号最大的提案的Value。如果所有的响应中都没有提案,那么此时V就可以由Proposer自己生成。
Acceptor接受
Acceptor可以忽略任何请求而不用担心破坏数据的一致性。
而对于接受的场景:
- 当一个Acceptor没有响应任何编号大于N的Prepare请求,那么他就可以接受这个编号为N的提案。
- 也即对Proposer的承诺(1)
因此一个Acceptor只需记住:1. 已Accept的编号最大的提案 2. 已Prepare请求的最大编号。
Learner学习
最简单的方案是每个 Acceptor 接受提案后,就将该提案发送给所有 Learner。 当Learner收到的提案超过一半时,即认为该提案被选择。但这样需要(M*N)次的网络通信
也可以选择一个主Learner。Acceptor接受一个提案后,就将该提案发送给主Learner,主Learner再通知其他Learner。但主Learner一旦故障会导致系统无法运转(单点故障)。
方案三则是Acceptor发送提案给某个Learner集合,再由Learner集合进行转发,是前两个方案的结合。
保持前进
如前所述,按照Paxos算法的流程,可能会有两个Proposer依次提出编号递增的方案,导致Acceptor在prepare阶段通过请求,在accept阶段拒绝请求。任何proposal都无法被执行,形成活锁。
因此可以选择一个主Proposer,仅有它可以发出提案。
Multi-Paxos算法
在Basic Paxos中只能对一个值进行决议,决议的形成至少需要两次网络来回,在高并发情况下可能需要更多的网络来回,极端情况下甚至可能形成活锁。
实际应用中几乎都需要连续确定多个值,而且希望能有更高的效率。Multi-Paxos正是为解决此问题而提出。Multi-Paxos基于Basic Paxos做了两点改进:
- 针对每一个要确定的值,运行一次Paxos算法实例(Instance),形成决议。每一个Paxos实例使用唯一的Instance ID标识。
- 在所有Proposers中选举一个Leader,由Leader唯一地提交Proposal给Acceptors进行表决。这样没有Proposer竞争,解决了活锁问题。在系统中仅有一个Leader进行Value提交的情况下,Prepare阶段就可以跳过,从而将两阶段变为一阶段,提高效率。
Multi-Paxos首先需要选举Leader,Leader的确定也是一次决议的形成,所以可执行一次Basic Paxos实例来选举出一个Leader。选出Leader之后只能由Leader提交Proposal,在Leader宕机之后服务临时不可用,需要重新选举Leader继续服务。在系统中仅有一个Leader进行Proposal提交的情况下,Prepare阶段可以跳过。
Multi-Paxos通过改变Prepare阶段的作用范围至后面Leader提交的所有实例,从而使得Leader的连续提交只需要执行一次Prepare阶段,后续只需要执行Accept阶段,将两阶段变为一阶段,提高了效率。为了区分连续提交的多个实例,每个实例使用一个Instance ID标识,Instance ID由Leader本地递增生成即可。
Multi-Paxos允许有多个自认为是Leader的节点并发提交Proposal而不影响其安全性,这样的场景即退化为Basic Paxos。
参考
[分布式一致性算法——Paxos原理与推导过程]
[Paxos算法详解]
[拜占庭将军问题]
[拜占庭将军问题深入探讨]