Paxos——分布式一致性算法

Google Chubby的做者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。算法

Paxos算法问世已经有将近30年的历史了,是目前公认最有效的解决分布式场景下一致性问题的算法之一,可是缺点是比较难懂,工程化比较难。本文但愿可以辅以图例和通俗易懂的实例把Paxos算法讲清楚。安全

Paxos算法的价值


在分布式系统中,在异步通信的过程当中,总会发生网络波动、机器宕机等状况,那么如何在这样复杂的状况下,快速且安全的就某一数值达成一致呢?Paxos算法主要就是解决此类问题,在布式锁、主从复制、命名服务、分布式协调等常见场景下,Paxos算法都有着普遍的应用。网络

基本概念


参与角色异步

在Paxos算法中,全部的参与者被分为了如下几个角色分布式

角色 分工 参与决策
Proposer 提出提案,提案:[编号Id,提议的Value]
Acceptor 接收提案,批准/拒绝提案,当提案被大多数的Acceptor(Quorum)批准后即为被选定的提案(Chosen)
Learner 学习(Learn)最新被选定的提案 ×
  • 提案:提案是由编号及Value组成,Paxos算法须要咱们保证提案的编号Id全局惟一有序(具体有不少种实现,不在本文的讨论范围内)。
  • Quorum:直译为法定人数,在Paxos中意为任意两个Quorum都包含至少一个公共成员,能够理解为包含Acceptor集合中的大多数成员。如一共有2F+1位Acceptor,则Quorum人数为F+1位。
  • Proposer、Acceptor、Learner只是角色的分工,在具体实现中,一个进程可能担当不止一种角色。

Paxos算法正确的必要条件学习

如今将算法的参与者分为了这样三个角色,那么是为了让他们完成什么样的工做目标呢?3d

一个分布式算法有两个最重要的属性:活性、安全性code

  • 活性意为“预期的事情最终必定会发生”,最终一致性就是一种活性。
  • 安全性意为违背了安全性规则,则系统会发生损失。

咱们能够从这两个方面来考察Paxos算法的正确性cdn

活性:blog

​ 保证最终有一个提案会被选定,当提案被选定后,进程最终最终也能获取到被选定的提案。

安全性:

  • 提案(value)只有在被 proposers 提出后才能被批准。
  • 在一次 Paxos 算法的执行实例中,只批准(chosen)一个 value。
  • learners 只能得到被批准(chosen)的 value。

那么咱们下面来看看具体的算法流程

算法流程


算法描述来自于倪超《从Paxos到ZooKeeper分布式一致性原理与实践》

提案的提出和批准
  • 阶段一

    1. Proposer选择一个提案编号N,而后向半数以上的Acceptor发送编号为N的Prepare请求。
    2. 若是一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的全部Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(若是有的话)做为响应反馈给Proposer,同时该Acceptor承诺再也不接受任何编号小于N的提案。
  • 阶段二

    1. 若是Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value,若是响应中不包含任何提案,那么V就由Proposer本身决定。

    2. 若是Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求作出过响应,它就接受该提案。

提案的发布
  1. acceptors须要将accept消息发送给learners的一个子集,而后由这些learners去通知全部learners。
  2. 可是因为消息传递的不肯定性,可能会没有任何learner得到了决议批准的消息。当learners须要了解决议经过状况时,可让一个proposer从新进行一次提案。注意一个learner可能兼任proposer。

干巴巴的算法描述可能比较难以理解,因此从图解分布式一致性协议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。

他们之间的关系能够用下图来讲明

图二.png

当Acceptor仅可批准一个提案时,仅依靠P1,也是可以只批准出一个Value的,可是在这种状况下,颇有可能须要屡次重复投票过程才可以达到一致性的状态,也就是说虽然可以保证安全性,可是牺牲了部分的活性。以下图所示:

图三.png

Proposer老是可以优先得到同机房内的Acceptor的批准,可是很难得到其余机房的Acceptor的批准,这时ProposerA、ProposerB、ProposerC各得到一票,每一个Proposer的提案都没有经过,此时Proposer只能生成编号更大的提案,以期许可以得到大多数的Acceptor(2个)的批准,也许将来不久,某个lucky dog最终可以得到大多数的Acceptor的批准,可是咱们已经等的花儿都谢了。

因此为了可以快速到达一致性,又引入了P2c和P1a,在P1a中解除了Acceptor只能批准一个提案的限制,可是增长了对于批准提案的编号的限制,在P2中增长了对Proposer提出提案的Value值的限制,这两个限制带来的直接效果有两个:

  1. 本来Proposer只须要和Acceptor交互一次,如今变成了两次,在Proposer正式提交提案前,Proposer要先得到大多数的Acceptor的状态(prepare请求),以确保提出的提案时,没有已经经过了的提案。由于是大多数的Acceptor,因此若是有已审批的提案,那么必定可以经过这批Prepare请求获知,若是得知已经有审批过的提案,那么表明Proposer已获知本次Paxos执行实例中的决议,并将本身的提案的Value值替换为已审批过的提案的Value值,保证安全性。
  2. 由于Proposer在正式提交提案前,已经通过了“严格”的问询和保证,Acceptor也会对审批的编号作审核,因此即便Acceptor可以批准多个提案,可是会保证审批经过的提案的值都具备相同的Value值。从而保证了安全性。

这样讲可能仍是比较难以理解,咱们如今就上面那个例子作一个图示,分别看看选出提案为A、和提案为B的流程。

  • P表明Acceptor对Proposer的Promise
  • A表明Acceptor对Proposer提案的Accept
  • PE表明保证失败,即图一中的
  • AE表明审批失败,即图一中的
  • 提案编号由时间戳和机器Id组成,如编号为1.2,则表明在时间戳为1时,机器Id2提出的提案。
  • 字母右边的数字表明提案编号,如P1.1表明Acceptor对于编号为1.1提案的Promise
  • 中括号[]内为回应内容,如P1.1[1.2:A]表明Acceptor对于编号1.1提案的Promise,并回应“我已经审批经过了编号为1.2,值为A”的提案。

图四.png

如图四所示,最终造成了值为A的提案。

图五.png

如图五所示,最终造成了值为B的提案。

这时候停下来思考一下,严格来讲,上面描述的牺牲活性问题并无解决,只是下降了发生了的几率,在极端状况下仍是可以发生一种相似于“活锁”的状态的。以下图所示

图六

在极端状况下,这种循环会一直进行下去。因此为了解决这种问题,又提出了Multi-Paxos算法,Multi-Paxos具体算法在这里不作陈述,它是在Proposer中又搞了一个Leader的概念,在初期,全部的Proposer中竞选出一个Leader,而后只有Leader可以向Acceptor提出提案,当Leader发生问题时,再竞选一个Leader出来,没有了Proposer的竞争,两阶段也变成了一阶段,提升了效率,也解决了活锁的问题。可是仔细想一想,竞选Leader的过程当中也可能会发生活锁的,我估计这也是Raft算法被提出来的真正缘由(狗头),毕竟最后绕了一大圈,最终仍是搞出了单点的Leader出来进行管理,还不如用上面P1+重试的机制选出Leader,效率平时是差很少的,仅在选举Leader时会比较慢而已。

总结


本文分析了Paxos算法的应用价值和具体实现原理,但愿能让你们在学习Paxos算法的过程当中可以少掉一点头发。后续可能还会更新我对Raft算法的理解。


欢迎关注个人博客:王老魔的代码备忘录

相关文章
相关标签/搜索