基本原则
提案 = 编号 + value
P1:每个Acceptor必须接受(accept)第一个提案(Proposal)
每个Acceptor可以接受多个提案
一个提案被选定(choose)需要该提案被半数以上的Acceptor接受
允许多个提案被选定,但是这些提案的value必须相同
P2:如果一个value为V的提案被选定,那么所有编号更大的被选定的提案的value必须也是V
P2a:如果一个value为V的提案被选定,那么所有编号更大的被接受的提案的value必须也是V
P2b:如果一个value为V的提案被选定,那么所有编号更大的提案的value必须也是V
P2c:对于任意的Nn和Vn,如果提案[Nn, Vn]被提出,那么存在一个由半数以上的Acceptor组成的集合S,满足以下两个条件中的任意一个:
- S中没有Acceptor接受过编号小于Nn的提案
- S中所有Acceptor接受过的编号小于Nn的提案中,编号最大的提案的value为Vn
P2c => P2b的证明
假设提案[N0, V0]被选定,在满足P2c的前提下,证明对于任意满足Nn > N0的提案[Nn, Vn],Vn = V0。
数学归纳法证明:
- 由于提案[N0, V0]被选定,所以存在一个多数集,其中每个Acceptor都接收了提案[N0, V0]。根据抽屉原理,任意一个多数集中都有Acceptor接受过提案[N0, V0](即编号小于Nn的提案),所以P2c中的条件一不成立,条件二成立。条件二成立意味着Vn等于某个多数集S中编号小于Nn但是编号最大的提案的value。
- 当Nn = N0+1时,如前所述,S中一定有Acceptor接受过提案[N0, V0],所以提案[N0, V0]就是S中编号小于Nn但是编号最大的提案,即Vn = V0。
- 当编号从N0+1到Nn-1的提案的value为V0时,我们证明对于编号为Nn的提案,Vn = V0。令S中小于Nn但是最大的编号为Nm,如果Nm落在[N0+1, Nn-1],那么对应的Vm = V0;如果Nm不落在[N0+1, Nn-1],那么Nm <= N0。注意到S中一定有编号N0,我们又有Nm >= N0,所以Nm = N0,对应的Vm = V0,即Vn = Vm = V0。
- 综上所述,证毕。
算法流程
阶段一:
- Proposer选择一个提案编号N,然后向半数以上的Acceptor发送编号为N的Prepare请求。
- 如果一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的所有Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给Proposer,同时承诺不再接受任何编号小于N的提案。
阶段二:
- 如果Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对提案[N, V]的Accept请求给半数以上的Acceptor。注意,V就是收到的响应中编号最大的提案的value,如果响应中不包含任何提案,那么V就由Proposer自己决定。
- 如果Acceptor收到一个针对提案[N, V]的Accept请求,只要该Acceptor还没有对编号大于N的Prepare请求做出过响应,它就接受这个提案。
阶段 | Proposer (N, V) | Acceptor (ResponseN, AcceptN, AcceptV) |
---|---|---|
阶段一 | 发出Prepare(N),N全局唯一且递增 | |
若N > ResponseN,令ResponseN = N,响应(Pok, AcceptN, AcceptV)或Pok 若N <= ResponseN,不响应或响应error | ||
阶段二 | 若收到半数以上的Pok,发出Accept(N, V) 若未收到半数以上的Pok,重发Prepare(N) | |
若N >= ResponseN,令AcceptN = N,AcceptV = V,响应Aok 若N < ResponseN,不响应或响应error | ||
若收到半数以上的Aok,确定V被选定 若未收到半数以上的Aok,重发Prepare(N) |
C语言实现
https://bitbucket.org/sciascid/libpaxos
参考资料
https://book.douban.com/subject/26292004/
http://www.jianshu.com/p/d9d067a8a086