paxos算法是进入分布式领域的一块基石,有关paxos的讨论有很多精彩的详细论述,很多牛人不惜宝贵时间以大幅详尽段落叙述。感谢他们,paxos more simple
理解paxos前,我建议以面到点的方式了解一些相关性主题
FLP:https://www.the-paper-trail.org/post/2008-08-13-a-brief-tour-of-flp-impossibility/
CAP:https://en.wikipedia.org/wiki/CAP_theorem
ACID:https://en.wikipedia.org/wiki/ACID_(computer_science)
BASE:https://en.wikipedia.org/wiki/Eventual_consistency
wiki:https://zh.wikipedia.org/wiki/Paxos%E7%AE%97%E6%B3%95
有时间可以看看paper
paxos made simple:https://www.microsoft.com/en-us/research/publication/paxos-made-simple/ (强烈建议看看)
这篇paper已经说得很清晰了,形式化逻辑证明。分布式的异常很多,通过枚举是难以完备的,这也是数学逻辑快刀斩乱麻的魅力所在
下面仅仅就paxos made simple谈一些局部问题
如果你已经知道了paxos的实际操作方法(propose --> prepare --> promise --> accept),请暂且不要以这种操作方法为定义域和上下文理解下面的内容
P1. An acceptor must accept the first proposal that it receives
注:对于多个proposer提议多个proposal并从中选定一个,最简单的方法是搞一台服务器作为acceptor,各个proposer向这台acceptor发送提议,最先到达的被选定。很多应用确实是这么做的并在此基础上做了优化。例如:热备份同步到另外一台机器做稳定性上的提升
如果只有一台acceptor服务器,会面临单点(single point)问题,acceptor挂了,整个系统就停了.至于热备份和自动切换机制,这种系统缺乏可扩展性,分布式系统的一大要求就是:scalability
如果有多个proposer提出多个proposal,对于accept而言丢几个没关系,甚至可以用 not accept的方法解决多个proposal竞争问题,例如:accept都拒绝接受前几个,只接受最后几个或者一个,但是分布式一致算法需要解决所有可能出现的问题,如果只有一个propose,拒绝接受会导致在这种最简单的case下无法选出一个proposal。所以P1是一个基本的prerequisite
到此为止,我们的思路是只要有一个acceptor accept了proposal,那么这个proposal就被选定了(chosen)
注:不要以实际操作方法的经验理解,paxos算法是先从最基本简单入手,逐步条件加强解决冲突,最后加强到或者引出实际可操作方法的过程)
P1会导致以下问题:
proposer1 ----v1----> acceptor1
proposer2 ----v2----> acceptor2
proposer3 ----v3----> acceptor3
三个acceptor都接受和选定了对应的proposer,这相当于把proposer之间的竞争转移到acceptor,公婆各有理,聊不出所以然,从整个系统看并没有选定出最终的proposal
所以我们需要改变选定(chosen)的定义,我们定义:被大多数acceptor accept的proposal称为选定(chosen)
这个定义有唯一性,如果(第一轮或者instance1)proposer1被chosen,那么proposer2必然不会被chosen,否则存在C1作为选定proposer1的大多数,C2作为选定proposer2的大多数,这两个集合的交集必然不为空,交集里面的元素同时accept了proposer1和proposer2
注:也许同时accept多个proposal能开发出另一种一致性算法,但目前我们仅讨论仅能accept一个proposal的情况,acceptor是程序现实的,如何让它仅accept一个proposal有多种方法,1.第一次accept后不再accept其他任何proposal。2.可以多次accept多个proposal,但每次accept的都是相同的value,由于是相同的value,从逻辑上满足仅accept了一个
至于这个大多数(majority)当然是在保证C1和C2有交集的前提下越小越好
P2:If a proposal with value v is chosen, then every higher-numbered proposal that is chosen has value v
P2的意思就是上面accpet采用的第二种方法,它满足了唯一性(一致性),同时也让accpet更为灵活,可以多次accept并且不改变最初已经被chosen的那个value
注:也许你会觉得既然已经有一个value被chosen,那直接告诉各个proposer让它不要再propose了不更直接么?是的,这确实是一种方案,但是1.继续要求accept相同的value不影响一致性 2.这里需要一个通知机制,这种机制在paxos的实现中可以作为一种补充和优化,分布式中每个节点是对等的,paxos算法是异步通信,在这种通知机制通知到各个proposer前,proposer是不知道整个系统是否已经有proposal chosen,所以继续propose并accept不失为一种通用方法,毕竟在那个proposal被chosen前正是靠各个propose来达到的
P2是一种要求,符合这个要求就能保证一致性,就像你的boss给你提了一个需求,需求本身不是解决方法,你自己要找出解决方案。其实看到P2有人会感觉这就是句废话,我也想这样啊,问题是怎么达到这个呢?
我们知道在chosen前必须先被accept,我们把范围放宽(条件加强)为P2a
P2a: If a proposal with value v is chosen, then every higher-numbered proposal accepted by any acceptor has value v
P2a这个要求相比P2貌似好了一点,起码没有以最后的结果当起点条件,但P2a也让我们很被动(臣妾做不到啊),accept什么value是proposer自己propose的,acceptor只是被动的接收,让acceptor来判断接收哪些value才能达到P2a也许有其他方法,但最自然的方法是把约束放到最初提议value的propose阶段,让proposer在propose前先自己问问各个accept,综合它们的反馈看看有没有proposal已经被majority chosen了,如果有就用chosen的那个value
P2a和P1有个冲突
proposer1 ----v1----> acceptor1
proposer2 ----v2----> acceptor2
proposer2 ----v2----> acceptor3
proposer2的value2已经被majority accept从而chosen,但由于各种网络分区问题,proposer2没有被acceptor1接收到,acceptor1仅accept了proposer1,这也正是P2a的被动之处
现在进一步把条件加强
P2b:If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v
P2b这个要求相比P2a更具体更具可操作性,而且解决了上面的冲突,剩下来的问题就是如何在实际操作中以一种操作方法满足P2b,从而满足P1,P2b-->P2a-->P2,Lamport提出了P2c
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
也就是在proposer 提出proposal前需要找到一个包含了majority acceptor的集合S,S满足 (a) 或者 (b),(a)其实是还没有proposal chosen的情形,proposer可以propose自己的value,(b)要求一个proposer在propose前必须先找出已经被accept的所有value中对应的提议编号最大的那个value。
我们先假设满足P2c能够推导出满足P2b,即:P2c ----> P2b
我们现在要做的就是如何在实际操作中找到满足P2c条件的操作流程,找到一个S并确定其中最大的那个number对应的value。
于是提出了两阶段请求:
phase 1.prepare 请求
phase 2.accept 请求
显然prepare请求就是了解各个acceptor已经accepted那些proposal和对应的value,这里要说的是acceptor 回应的(ACK)的promise,即:不再accept 比当前prepare请求对应的 number低的proposal
异步网络分布式中,有些数据包可能会延迟到达,如果没有promise的规则,acceptor会在回应了prepare请求后继续accept其他number更低的请求,这会造成给prepare返回的已accepted的信息和当前的信息不一致,从而不满足(b) ,promise是在保持一种不变性
两阶段请求满足了P2c,从而在实际操作中实现了一致性。这里还有很多值得讨论的地方,例如:accept请求为何能被更高的prepare请求抢占(preempt),不允许被抢占又如何等等。这里要以整个系统的角度看各个阶段,单看各个阶段并不能很好的论述这些问题。paxos算法只是在异步网络分布式各个node对等的情形下达到一致性的一种算法,它有一整套系统的操作方法,至于这些操作方法中的某些阶段能否修改或者发展出另一种一致性算法是题外话
最后就是用第二数学归纳法证明P2c-->P2b
P2,P2a,P2b都只是要求(蓝图),P2c是实际实现这个要求的方案,下面证明这个方案能实现要求
命题:假设 (P0, V0),已经被chosen,证明任何比该提议号大的proposal (Pn, Vn)在满足P2c的前提下,有Vn=V0
证明:
1.当Pn = P0 +1 时,因为(P0, V0)已经chosen,所以存在一个大多数acceptor集合S0,S0中的每一个acceptor均accept了(P0, V0),现在(Pn, Vn)按P2c的(b)提出,所以存在Sn,Sn和S0的交集不为空,根据P2c的(b),最大的提议号必然>=P0,因为此时P0已经是最大的,所以Pn选的value即为V0
2.假设 P0+1到Pn-1区间内所有proposal的value均为V0,(Pn, Vn)满足P2c (b)对应的Sn必然和S0有不为空的交集,所以Pn收集的最大的提议号>=P0,如果等于P0显然Vn=V0,否则提议号位于 [P0+1, Pn-1]区间,由假设可得Vn=V0
综合1,2 得证
上面说的是proposer的算法,对于acceptor的算法可以由P1扩展为P1a
P1a:An acceptor can accept a proposal numbered n iff it has not responded to a prepare request having a number greater than n
显然P1a --> P1
paxos算法可以归结为一个简单的实事:
if a majority of acceptors accepted the same request, an instance is closed.
Any proposer trying to do something for that instance will realize during phase1 that there is a value already. It is then forced by the protocol to help with the current situation rather than try to push a different client value