前言:Paxos算法很重要,但变种也很多。近来找到Lamport大神的原版论文加以梳理。 这篇论文不光解释了Paxos算法,更重要的是阐述了这个算法是如何被思考出来的。论文link:http://lamport.azurewebsites.net/pubs/paxos-simple.pdf
Consensus问题在distributed system领域非常关键,这篇论文是Lamport大神描述的Paxos算法。
背景材料:我们将agent分为三类:proposer, acceptor, learner.
简单来说,proposer负责提出proposal,acceptor好像是memory进行记录,learner是最后决定的值。
算法思考的思路:
0. 最简单直观的一种想法:使用一个acceptor存放proposal,把它收到的第一个proposal作为决定值。但是这个方法有一个致命的缺点就是:单点故障导致系统不可用。
1. 所以,我们想找到另一种方式:使用multiple acceptor来“存放”proposal。然后,当一个value被majority的acceptor“接受”之后,它就被“chosen”。
2. 有了这个想法,在理想情况下(即没有failure,没有message loss,只有一个proposed value):我们必须要有这个要求:
P1. An acceptor must accept the first proposal that it receives
3. 如果没在理想环境下呢?当有多个proposer“几乎同时”propose value的时候呢?只有P1就会出现问题。系统可能没有一个value被”Majority”接受(设想有三个proposer同时提议,每个value只被接受了1/3)。
小结一下,我们现在的条件:
( A value is chosen only when it is accepted by a majority of acceptors, P1)
=> An acceptor must be allowed to acceptmore than one proposal.
即通过我们的目标和P1,我们得出,一定要让acceptor能够“accept”超过一个的proposal(防止多个proposal“各自圈地盘不交出来”)。
4. 如果允许一个acceptor “accept”多个proposal,我们就需要有一个方式区别proposal。这里,我们假设不同的proposal有不同的number(如何实现是implementation层面的,比如让不同的proposer取互不相交的数集)。根据以上的条件和目的,我们要解决问题,我们需要保证:
A value is chosen when the proposal is accepted by the majority.
稍微“变通”一点的要求就是保证:
We can allow multiple proposals to be chosen, but we must guarantee that all chosen proposals have the same value.
结合proposal的编号,只让相同value的proposal被“chosen”。我们限定出如下条件即可:
P2. If a proposal with value v is chosen, then every higher-numbered proposal that is chosen has value v.
5. 但是P2的条件很抽象,不好直接实现。所以当我们“强化限定”P2,只让相同value的proposal被“accepted”, 即:
P2a. If a proposal with value v is chosen, then every higher-numbered proposal that accepted by any acceptor has value v.
但因为系统是“asynchronous”。设想一个“空白的”acceptor刚“清醒”,收到了一个high-numbered proposal with different value. 那么无论它“accept or not”,它都会违反P1或者P2a其中的一项。所以我们想要继续“强化限定”P2a.
6. 只让相同value的proposal被“issued”。
P2b. If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v.
我们可以看出来:
P1b => P1a=> P1
7. 接下来,我们想更加“明确”地知道如何才能保证P2b.
我们先回忆几个场景:
当一个value被chosen的时候,一定是”majority”的acceptor accept了它。所以, 对于每个被chosen的value(假设是在编号m次接受的),都存在一个对应的集合C:
1) 这个C是个“majority”。
2) 每个C中的元素都accept了这个value
3) Every acceptor in C has accepted a proposal with number in m..(n-1), and every proposal with number in m..(n-1) accepted by any acceptor has value v.
通过思考,我们可以这么来保证P2b:
P2c. For any v and n, if a proposal with value v and number n is issued, then there is a set S consisting of a majority of acceptors such that either(a) no acceptor in S has accepted any proposal numbered less than n, or (b)v is the value of the highest-numbered proposal among all proposals numbered less than n accepted by the acceptors in S.
简单说来,什么时候一个proposal才可以被issue呢?
- 或者 存在一个majority setS:所有的acceptor都还没有“接受”过value(即大部分都是acceptor)
- 或者 存在一个majority setS:集合中the highest-numbered proposal的value值等于当前proposal的value值(即,之前接受的最新值就是v)。
设想一下,当我们有上面P2c的限定之后,我们就可以控制什么时候才能issue proposal。而完全按照P2c的话,就可以达到P2b。
综上,有以下关系:
P2c => P2b => P2a => P2
8. 接下来,我们来查看算法的实现步骤,论文中的论述比较复杂,而且算法之后的优化说的也很详细.
整体说来,算法分为两个部分:Prepare 和 accept:
描述下来,算法的原理就是让通过prepare和accept两个phase来保证P1, P2c。算法的执行过程如下:
1) Proposer:先发出”prepare(n)”请求大一个majority(其实,这里发送给所有acceptor也可以)
2) Acceptor:收到prepare(n)请求之后:
a) 如果自己没有收到过”Prepare”,直接promise不会接受比n小的请求,返回
b) 如果已经收到过请求而且编号小于n,那么返回它“accept”的最大编号和这个v.
3) Proposer:如果它收到了一个“majority”的acceptors给的回复,发送accept_request(n, v)。这个v可能是:
a) 回复中的编号最大值的v
b) 若回复里没有值,将v设为任意想要发送的值
4) Acceptor: accept 这个proposal当:
a) 没有Prepare过编号更大的proposal
上面的算法是论文中的算法,其中,有许多需要细节完善的地方,可以使用不同的做法:
1) 不同的proposal有不同的编号,怎么指定编号?论文中说可以让不同的proposer在互不相交的数集中找编号。比如p1使用“n+1, 2n+1, 3n+1”, p2使用“n+2, 2n+2, 3n+2”......
2) 如何choose最后的值?可以有多种方式,比如让每个acceptor都给所有learner发自己的决定信息(这样大大增加了系统的负担)。又比如,找出若干“特殊的learner”负责choose值,然后通知给其余的learner。
3) Progress?如何保证这个算法能最后达到结果呢?我们可以设想一种场景:两个proposer交替的Prepare,那么,没有一个proposer能成功。在这种情况下,Consensus的liveness就无法保证。我们如何做呢?我们可以使用Leader机制。让一个Leader来为Client发送proposal.那么,我们如何保证有Leader呢?这就靠Eventual leader election (Ω)来保证最终会选出一个leader。所以,算法的场景变成这样:一开始,全局leader没选出的时候,每个client委托“自己看到的leader”来propose(使用Paxos算法来解决这几个proposer的问题)。之后,Ω(eventual leader election <=> FD <>S <=> FD <>W)保证liveness, 会选出最后的leader。
上面的算法总是不是很直观,各个roles也混在一起。经过简化和合并,找到一个论文外的Paxos图:
我们可以看到上面的算法实践中,acceptor会发送ack或者reject给proposer。其实,这有点像把proposer当做“特殊的learner”。但当我们透过表象看本质,发现,原理还是论文中所阐述的。只是在原理的基础上,可以有不同的实现。