Google Chubby的做者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。算法
Paxos算法问世已经有将近30年的历史了,是目前公认最有效的解决分布式场景下一致性问题的算法之一,可是缺点是比较难懂,工程化比较难。本文但愿可以辅以图例和通俗易懂的实例把Paxos算法讲清楚。安全
在分布式系统中,在异步通信的过程当中,总会发生网络波动、机器宕机等状况,那么如何在这样复杂的状况下,快速且安全的就某一数值达成一致呢?Paxos算法主要就是解决此类问题,在布式锁、主从复制、命名服务、分布式协调等常见场景下,Paxos算法都有着普遍的应用。网络
参与角色异步
在Paxos算法中,全部的参与者被分为了如下几个角色分布式
角色 | 分工 | 参与决策 |
---|---|---|
Proposer | 提出提案,提案:[编号Id,提议的Value] | √ |
Acceptor | 接收提案,批准/拒绝提案,当提案被大多数的Acceptor(Quorum)批准后即为被选定的提案(Chosen) | √ |
Learner | 学习(Learn)最新被选定的提案 | × |
Paxos算法正确的必要条件学习
如今将算法的参与者分为了这样三个角色,那么是为了让他们完成什么样的工做目标呢?3d
一个分布式算法有两个最重要的属性:活性、安全性code
咱们能够从这两个方面来考察Paxos算法的正确性cdn
活性:blog
保证最终有一个提案会被选定,当提案被选定后,进程最终最终也能获取到被选定的提案。
安全性:
那么咱们下面来看看具体的算法流程
算法描述来自于倪超《从Paxos到ZooKeeper分布式一致性原理与实践》
阶段一
阶段二
若是Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value,若是响应中不包含任何提案,那么V就由Proposer本身决定。
若是Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求作出过响应,它就接受该提案。
干巴巴的算法描述可能比较难以理解,因此从图解分布式一致性协议Paxos这里借来一个很简明的图来辅助理解。
从上图看到,做为Acceptor只须要存储批准/保证过的提案的最大编号(MaxN),批准过的提案的最大编号(AcceptN),批准过的提案的Value值(AcceptV),而后经过阶段一(2)和阶段二(2)的两种规则进行对提案的审批,即可以保证审批的安全性。
而Proposer须要保证在阶段一(1)时提出的提案编号惟一且单调递增,而在阶段二(1)时只对获取到了足够多的保证(即得到了大多数Acceptor对Proposer的保证)的提案进行提交,即可以保证提案申请的安全性。
那么为何这样可以知足上面所述的分布式算法的安全性呢?这个要从Paxos算法的推导来看。完整的推导过程能够在wikipedia上看到。
下面我来谈一谈个人理解,在推导过程当中有这么几个重要的约束:
P1:一个 acceptor 必须接受(accept)第一次收到的提案。
P1a:当且仅当acceptor没有回应过编号大于n的prepare请求时,acceptor接受(accept)编号为n的提案。
P2:一旦一个具备 value v 的提案被批准(chosen),那么以后批准(chosen)的提案必须具备 value v。
P2a:一旦一个具备 value v 的提案被批准(chosen),那么以后任何 acceptor 再次接受(accept)的提案必须具备 value v。
P2b:一旦一个具备 value v 的提案被批准(chosen),那么之后任何 proposer 提出的提案必须具备 value v。
P2c:若是一个编号为 n 的提案具备 value v,那么存在一个多数派,要么他们中全部人都没有接受(accept)编号小于 n 的任何提案,要么他们已经接受(accept)的全部编号小于 n 的提案中编号最大的那个提案具备 value v。
他们之间的关系能够用下图来讲明
当Acceptor仅可批准一个提案时,仅依靠P1,也是可以只批准出一个Value的,可是在这种状况下,颇有可能须要屡次重复投票过程才可以达到一致性的状态,也就是说虽然可以保证安全性,可是牺牲了部分的活性。以下图所示:
Proposer老是可以优先得到同机房内的Acceptor的批准,可是很难得到其余机房的Acceptor的批准,这时ProposerA、ProposerB、ProposerC各得到一票,每一个Proposer的提案都没有经过,此时Proposer只能生成编号更大的提案,以期许可以得到大多数的Acceptor(2个)的批准,也许将来不久,某个lucky dog最终可以得到大多数的Acceptor的批准,可是咱们已经等的花儿都谢了。
因此为了可以快速到达一致性,又引入了P2c和P1a,在P1a中解除了Acceptor只能批准一个提案的限制,可是增长了对于批准提案的编号的限制,在P2中增长了对Proposer提出提案的Value值的限制,这两个限制带来的直接效果有两个:
这样讲可能仍是比较难以理解,咱们如今就上面那个例子作一个图示,分别看看选出提案为A、和提案为B的流程。
如图四所示,最终造成了值为A的提案。
如图五所示,最终造成了值为B的提案。
这时候停下来思考一下,严格来讲,上面描述的牺牲活性问题并无解决,只是下降了发生了的几率,在极端状况下仍是可以发生一种相似于“活锁”的状态的。以下图所示
在极端状况下,这种循环会一直进行下去。因此为了解决这种问题,又提出了Multi-Paxos算法,Multi-Paxos具体算法在这里不作陈述,它是在Proposer中又搞了一个Leader的概念,在初期,全部的Proposer中竞选出一个Leader,而后只有Leader可以向Acceptor提出提案,当Leader发生问题时,再竞选一个Leader出来,没有了Proposer的竞争,两阶段也变成了一阶段,提升了效率,也解决了活锁的问题。可是仔细想一想,竞选Leader的过程当中也可能会发生活锁的,我估计这也是Raft算法被提出来的真正缘由(狗头),毕竟最后绕了一大圈,最终仍是搞出了单点的Leader出来进行管理,还不如用上面P1+重试的机制选出Leader,效率平时是差很少的,仅在选举Leader时会比较慢而已。
本文分析了Paxos算法的应用价值和具体实现原理,但愿能让你们在学习Paxos算法的过程当中可以少掉一点头发。后续可能还会更新我对Raft算法的理解。
欢迎关注个人博客:王老魔的代码备忘录