分布式协议:Paxos

提出问题

在分布式系统中如何实现跨进程通讯,这是一个经典问题。主要的解决方案包含了两种:web

  • 共享内存
  • 消息传递

本篇不记录关于共享内存的方式,将从消息传递方式引出paxos算法。在基于消息传递通讯模型的分布式系统中,不可避免的会发生进程变慢、被杀死或者重启,消息可能会发生延迟、重复、丢失等问题。在一个典型的场景中,一个分布式数据库系统,若是各个节点初始状态一致,每一个节点都进行相同的操做,那么他们最后是一个一致的状态。为了保证每个节点都保证一致的状态,那么就须要在每一条指令上都执行一个共识算法以保证每个节点看到的指令(插入、更新、删除)都是一致的。一个通用的共识算法能够应用在许多场景中,是分布式计算中的重要问题。所以从20世纪80年代起对于共识算法的研究就没有中止过。算法

1982年一位叫Lamport的大神与另外两位做者发表了一篇论文The Byzantine Generals Problem,文中已较为晦涩的方式阐述了Paxos算法,由此打开了计算机在分布式一致性方面的大门,这也是目前公认的解决分布式一致性问题的有效办法,该论文较为难懂,如若想更好的理解还请参看Paxos Made Simple数据库

若想了解一致性算法,那么先要明确几个实际需求。假设有一组能够提出提案的进程,那么对于一个一致性算法来讲须要保证如下几点:异步

  • 若是没有提案被提出,那么就不会有被选定的提案。
  • 在一次被提出的多个提案中,只有一个提案可以被选中。
  • 当一个提案被选定时,其余进程可以获取到该提案。

带着这几个需求再来深刻了解Paxos算法。分布式

算法内容

在Paxos中共有三种角色参与,用 Proposer、Acceptor、Learner 表示(固然,容许身兼多职),Proposer负责提出提案,提案包括了提案编号和提案内容(好比:提案001-将税率提高10%),Acceptor能够接收提案,若提案被多数的Acceptor接受,那么将会被提交批准(chosen),Learner只可以从Acceptor那里学习被批准的提案。学习

上边的说法可能很差理解,能够将其理解为表明与群众的关系,表明能够是Proposer同时也是Acceptor,他们在会议上提出提案,由其余的表明一同决定是否统一该提案,若是多数赞成,那么该提案也就意味着经过,群众再从各个表明那里学习提案精神。明确了角色关系,再回头看以前的问题:优化

  • 约束1:决议(value)只有在被Proposer提出后才可以被批准(未经批准前的决议被称为提案)。
  • 约束2:在一次paxos算法(可能存在多个提案)执行的过程当中,只有一个提案会被批准。
  • 约束3:只有一个提案被批准成为决议时,Learner才可以获取到他。

推导过程

批准value的过程当中,首先Proposer将value发送给Acceptor,以后Acceptor对value进行接受。为了知足只批准一个value的约束,要求经多数派接受的提案value成为决议。这是由于不管是按照人数仍是按照权重划分,两组多数派至少有一个公共的Acceptor,若是每一个Acceptor只能接受一个value,约束2就能保证。.net

因而产生了一个显而易见的新约束:code

P1:一个Acceptor必须接受(accept)第一次收到的提案。

注意 P1 是不完备的。若是刚好一半Acceptor接受的提案具备valueA,另外一半接受的提案具备valueB,那么就没法造成多数派,没法批准任何一个value。进程

约束2并不要求只批准一个提案,暗示可能存在多个提案。只要提案的value是同样的,批准多个提案不违背约束2。因而能够产生约束 P2:

P2:一旦一个具备valuev 的提案被批准(chosen),那么以后批准(chosen)的提案必须具备valuev。

注:经过某种方法能够为每一个提案分配一个编号,在提案之间创建一个全序关系,所谓“以后”都是指全部编号更大的提案。

若是 P1 和 P2 都可以保证,那么约束2就可以保证。

批准一个value意味着多个Acceptor接受(accept)了该value。所以,能够对 P2 进行增强:

P2a:一旦一个具备valuev 的提案被批准(chosen),那么以后任何Acceptor再次接受(accept)的提案必须具备valuev。

因为通讯是异步的,P2a 和 P1 会发生冲突。若是一个value被批准后,一个Proposer和一个Acceptor从休眠中苏醒,前者提出一个具备新的value的提案。根据 P1,后者应当接受,根据 P2a,则不该当接受,这种场景下 P2a 和 P1 有矛盾。因而须要换个思路,转而对Proposer的行为进行约束:

P2b:一旦一个具备valuev 的提案被批准(chosen),那么之后任何Proposer提出的提案必须具备valuev。

因为Acceptor能接受的提案都必须由Proposer提出,因此 P2b 蕴涵了 P2a,是一个更强的约束。

可是根据 P2b 难以提出实现手段。所以须要进一步增强 P2b。

假设一个编号为 m 的valuev 已经得到批准(chosen),来看看在什么状况下对任何编号为 n(n>m)的提案都含有valuev。由于 m 已经得到批准(chosen),显然存在一个Acceptor的多数派 C,他们都接受(accept)了v。考虑到任何多数派都和 C 具备至少一个公共成员,能够找到一个蕴涵 P2b 的约束 P2c:

P2c:若是一个编号为n的提案具备valuev,该提案被批准(chosen),那么存在一个多数派,要么他们中全部人都没有接受(accept)编号小于n的任何提案,要么他们已经接受(accept)的全部编号小于n的提案中编号最大的那个提案具备valuev

要知足P2c的约束,Proposer提出一个提案前,首先要和足以造成多数派的Acceptor进行通讯,得到他们进行的最近一次接受(accept)的提案(prepare过程),以后根据回收的信息决定此次提案的value,造成提案开始投票。当得到多数Acceptor接受(accept)后,提案得到批准(chosen),由acceptor将这个消息告知learner。这个简略的过程通过进一步细化后就造成了Paxos算法。

在一个paxos实例中,每一个提案须要有不一样的编号,且编号间要存在全序关系。能够用多种方法实现这一点,例如将序数和Proposer的名字拼接起来。如何作到这一点不在Paxos算法讨论的范围以内。

若是一个没有chosen过任何Proposer提案的acceptor在prepare过程当中回答了一个Proposer针对提案n的问题,可是在开始对n进行投票前,又接受(accept)了编号小于n的另外一个提案(例如n-1),若是n-1和n具备不一样的value,这个投票就会违背P2c。所以在prepare过程当中,acceptor进行的回答同时也应包含承诺:不会再接受(accept)编号小于n的提案。这是对P1的增强:

P1a:当且仅当acceptor没有回应过编号大于n的prepare请求时,acceptor接受(accept)编号为n的提案。

完整算法

经过一个决议分为两个阶段:

  • prepare阶段:

    • Proposer选择一个提案编号M并将prepare请求发送给Acceptor中的一个多数派(超过半数的子集);
    • acceptor收到prepare消息后,若是提案的编号M大于它已经回复的全部prepare消息(回复消息表示接受accept),则acceptor将本身上次接受的提案回复给Proposer(ACK),并承诺再也不回复小于M的提案;

      举例来讲,当前Acceptor已经回复了编号为一、二、三、四、5提案,那么该Acceptor在接收到编号为6的提案时,将会回复编号5,而且承诺不会再接收编号小于6的提案。

  • 批准阶段:

    • 当一个Proposer收到了半数以上Acceptor对prepare的回复后,它将要向回复prepare请求的Acceptor发送accept请求,包括编号n和根据P2c决定的value[M, value],若是根据P2c没有已经接受的value,那么value将能够是任意值。
    • 在不违背本身向其余Proposer的承诺的前提下,acceptor收到accept请求后即批准这个请求。

在实际运行中,每个Proposer都有可能产生多个提案,但只要系统按照当前算法运行,那么就可以保证正确性。若是一个Proposer已经生成了更大的提案编号,那么删除编号较小的是一个更好的选择,因此在Acceptor接收到一个编号为n的请求,但其发现已经接受了比n更大的编号,那么它会通知发送编号n提案的Proposer,让其将这个提案删除掉。

决议的发布

  • 一个显而易见的方法是当Acceptor批准一个value成为决议时,将这个消息发送给全部learners。也就是说当提案经过时,将会有至少Acceptors个数*Learner个数的通讯次数,这对一个分布式系统来讲并非十分友好。
  • 当前咱们不考虑拜占庭将军问题,learners能够经过别的learners进行通讯,获取已经经过的决议。所以Acceptor只需将批准的消息发送给指定的某一个learner(也就能够理解为主learner),其余learners向它询问已经经过的决议。这个方法下降了消息量,可是主learner存在单点问题,其下线将引发系统失效。所以Acceptor须要将accept消息发送给learners的一个子集,而后由这些learners去通知全部learners。
  • 可是因为消息传递的不肯定性,可能会没有任何learner得到了决议批准的消息。当learners须要了解决议经过状况时,可让一个Proposer从新进行一次提案。注意一个learner可能兼任Proposer。

可持续性的保证

根据上述过程当一个Proposer发现存在编号更大的提案时将终止提案。这意味着提出一个编号更大的提案会终止以前的提案过程。若是两个Proposer在这种状况下都转而提出一个编号更大的提案,就可能陷入活锁,也就是二者不停的拿出更大的提案,这违背了算法可持续性的要求。通常活锁能够经过随机睡眠-重试的方法解决。

不过在这种状况下的更好的解决方案是选举出一个leader Proposer,仅容许leader提出提案。这样一来只要主Proposer和过半数的Acceptor可以进行通讯,那么提案就可以被批准。

这种状况下的解决方案是选举出一个leader,仅容许leader提出提案。可是因为消息传递的不肯定性,可能有多个proposer自认为本身已经成为leader。这也就须要提供leader Proposer高可用。

总结

本文经过对分布式系统的消息传递模型分析,进而引伸出Paxos算法,Paxos经过引入过半原则,和基于2PC的机制,进一步优化了2PC和3PC中包含的问题,解决的同步阻塞、脑裂、无限期等待等问题。能够说Paxos算法是分布式一致性共识协议中较为理想的实现。

相关文章
相关标签/搜索