Paxos Made Simple【中文翻译注解】

paxos made simple

[ 翻译自 Leslie Lamport 论文

http://lamport.azurewebsites.net/pubs/paxos-simple.pdf  ,

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/paxos-simple-Copy.pdf

另外一篇翻译文章  https://segmentfault.com/a/1190000037628341]

 0概要

    paxos 算法通过日常(plain)英语来描述是简单的(simple)

 1 引言

   用于实现一个允许错误(fault-tolerant)的分布式系统的Paxos算法被认为是难于理解的,可能因为对许多读者来说原始的表述是一个希腊的故事[5]。实际上,这个算法是最简单最显而易见的分布式一致性算法之一。[5] (注解:paxos第一篇论文)中的选举(synod)算法的核心是一种一致性算法.下一节会展示这个一致性算法来自于(follows almost unavoidably)我们想要一致性算法需要满足的属性.最后一部分解释了完整的paxos算法,它是通过把易懂的一致性软件(application)用在构建分布式系统的状态机实现(approach)来得到的(obtained)。这种状态机实现应该是著名的,因为它可能是最常被引用的关于分布式系统理论的文章[4]的主题(subject)。

 2 paxos 一致性算法

[本文作者注解]

paxos 算法的前提

  消息传递异步无序(asynchronous): 消息可能丢失,可能重复,可能存在任意长时间的延迟,

  节点宕机(fail-stop): 节点持续宕机,不会恢复

  节点宕机恢复(fail-recover): 节点宕机一段时间后恢复,在分布式系统中最常见

  网络分化(network partition): 网络链路出现问题,将N个节点隔离成多个部分

  不存在byzantine failure (拜占庭异常) ,即消息不会被修改,也不存在内容损坏

  在fail-recover下,消息必须可以被进程持久化,并且持久化不会出错

[注释结束]

 2.1 问题描述

      假设有一组进程(processes), 每个进程都可以提出提议(propose values)。一致性算法保证所有提出的提议(valus)中,只有一个提议(one value)会被选择(chosen)。如果没有提出提议(proposed),那么将不会有提议被选择。如果一个提议被选择,那么所有process可以学习到(learn the chosen value)这个提议。一致性算法需要的安全性要求是  

       1)  仅可以选择被提出的提议(proposed value)
       2)  仅有一个提议(proposal)会被选择
       3)  进程(process )不会知晓一个值被选择了,直到这个值确实被选择了  (A process never learns that a value has been chosen unless it actually has been.)

 译者注: 

      这个问题的背景可以追溯到,分布式为什么要诞生?  因为如果不分布式 ,单机的话存在单点问题,可靠性缺乏保证,一方面是计算的单点,另外一方面是存储的单点.  所以需要多台机器和对应的存储,这些server和存储组成一个整体, 那么问题就来了?  client 随机向任意一个server发起调用,有如下问题
       1. 如何判断第一个指令应该给哪个server接受的客户端指令 

       2. 引入了这个决策后, 决策的结果,存储在哪, accepter挂掉了怎么办?

      第一问题,本文就是解答这个问题的. 第二个问题的答案在"learner选择这一节"译者注里给出,

       其次才是指令本身的计算和结果存储的问题, 机器挂掉后,数据是否丢失的问题? 读怎么读的问题? 各server指令能否乱序执行(后到的先执行,完全取决于是否占用同样的锁,同样的锁必须顺序,否则可以乱序,但关键是如果前面的命令你都不知道,你如何知道是否加同一个锁) ? 多个计算处于同一个事务怎么办? 这些问题都是后续问题了,其他文献会解答,先不扯开.

     我们将不会试图指明精准的活性(liveness)要求。然而,目标是确保最终某个提出的提议会被选择,并且选择好了一个提议后,进程(process)最终能够习得这个值(learn the value)。

    我们让三类代理(agent)来执行这个一致性算法中的三个角色:提议者(proposers)、接受者(acceptors)以及学习者(learners)。在实际实现中,一个独立的进程可以充当不止一个代理,但是从代理到进程之间的映射关系不是我们这里关注的重点。

   假设角色之间用消息(messages)通信。我们采用异步、非拜占庭模型

    * 角色以任意的速度运行(operate),可能会因为停机而失效(fail by stopping),可能会重启(restart)。因为所有的角色都可能会在提议被选择后停机(fail), 然后重启,因此任何一个解决方案都要求角色必须能够记住(remember)某些信息,从而能在重启后重新载入。

    * 消息的投递(deliver)可能会花费任意长的时间,可能会重复或丢失,但是内容不会损坏。

 2.2 选择一个值(choosing a value) 

     最简单的方式是仅有一个acceptor角色。Proposer发送提议(proposal)给这个acceptor,它选择最先收到的提议(proposed value)。尽管简单,但是如果这个acceptor停机了,那么系统就不能继续运行了,这个方案并不能满足要求。【明显的单点问题】

    所以,让我们来尝试选定值的另一种方法吧。不再是单一的接受者(acceptor),我们现在尝试使用多个接受者代理(acceptor agent)的方式。一个提议者(proposer)将一个提议的值发送给一群接受者。一个acceptor可以接受(accept)该提议,当有足够多的acceptor接受了这个提议时,提议就被选择了。那么需要多少才足够呢?为了保证只有一个决议可以被选择,任何多数派(majority)都满足。因为任意两个多数派至少共同拥有一个相同的acceptor,如果一个acceptor最多只能接受一个值,这就是可行的(it works)。(有一个明显的关于多数派的概括,这个概括存在于许许多多的论文中,明显是从[3]开始的)

    假设没有失败(failure)或者消息丢失,即使仅有一个proposer提出了一个提议,我们也希望能选择这个提议。这就导出了下面的要求:

 P1.  Acceptor必须接受(accept)它接收到的第一个提议。( P1. An acceptor must accept the first proposal that it receives)

    但是该要求会导致一个问题。同时可能有几个proposer提出了几个不同的决议,恰好导致每个acceptor都接受了一个提议,但是没有一个决议是被多数派(majority)接受。即使只有两个提议,如果每个都被半数的acceptor接受,单个的acceptor失效(failure)也会导致不可能知道到底哪个提议被选择(chosen)了(译者注解: 一个失效就导致达不到半数)。

选择 [译者 新增的二级目录] 

译者注: 选择这个概念很重要,是对整体而言的,大多数.  对单个角色  proposer : 1. prepare 2. 提议  acceptor: 接受 , 批准  learner:  学习

     一个提议要经过多数派的接受才能被选择(chosen),这个要求和P1暗示了acceptor必须能够被允许接受多个提议。我们为每个提议分配一个编号来记录不同的提议,因此一个议案由编号和值(value)构成【也就是提议={编号,值}】。为避免混淆,我们要求提议的编号是唯一的。这个取决于实现 ( 译者注: 参考 zoonkeeper  ,比较关键 ),现在我们假设可以做到这一点。如果一个提议{n, v}被多数派(majority)的接受,那么值v就被选择(chosen)了。这种情况下,我们称提议(包括其值v)被选择(chosen)了。

     [译者注解:

      下面几段其实都是在被选择的前提下分析, 先设一个结论A,然后分析如何让这个结论成立,提出一个什么新的结论B.  让后进一步分析结论B成立的条件 ] 

    我们可以允许多个提案被选定,但是必须保证所有被选定的提案拥有一样的值。结合提案编号归纳推理(By induction on the proposal number),,只要保证以下(结论)就够了(it suffices to guarantee):

     P2.如果一个提议(proposal){n, v}被选择(chosen),那么每一个编号更高的被选择的提议的值是v。       ( P2. If a proposal with value v is chosen, then every higher-numbered proposal that is chosen has value v. has value v 翻译为 拥有的值 =v)

      因为编号是完全有序(totally ordered)的,P2保证了“只有一个值(value)被选择”这一关键安全属性。提议必须至少被一个acceptor批准才可能被选择。所以想要满足(satisfy)P2,只要满足(satisfying)(下面的结论) :   

  P2a. 如果一个提议{n, v}被选择(chosen),那么每一个acceptor接受的编号更高的提议的值都是v。( P2a. If a proposal with value v is chosen, then every higher-numbered proposal accepted by any acceptor has value v.) 

[译者注: p2a 里的 "每一个accepter接受" 是 p2里的 "被选择" 的一种充分条件 ,某种意义上是换句话表达 . 这也是本文的撰写思路, 用后面的结论层层来保证前面的结论成立 ]     

     我们依然保持P1来确保(ensure)某个提议会被选择。因为通信是异步的,可能有这样的情况,某个接受者(acceptor)c 从没有收到(receive)过任何提议,但多数派已经接受了(accepted)了一个提议{n,v}。 假设这时一个新的proposer故障恢复(wakeup)并提议了一个更高编号的提议(包含不同的值value ) 。P1规则要求接受者(acceptor)c 应该接受这个提议,但是这违反了P2A  [译者注: 作者通过异常流程, 不停的补充新的规则和条件. 将思考过程展示给我们, 这种写作手法会使得文章会难理解, 以前译者就经常这么写, 但建议直接丢出结果,再来说明各种异常情况不成立 ] 。为了同时保证P1和P2A,我们需要增强P2A,补充条件如下:

    P2B. 如果一个提议{n, v}被选择,那么此后,任何proposer提出的编号更高提议的值都是v。( P2b. If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v.)

  译者注: P2A 里是"提出"是P2B里的提出的

  译者注: 该结论,用数学符号表达就是:   提议{m, v}被选择 (数学符号表达里的m 其实就是p2B里的n ) ,任何编号n>m的提议的值都是v (数学符号表达里n 就是 p2B 里的更高提议)

   因为一个提议必须在被proposer提出后才能被acceptor批准,因此P2B 蕴涵了P2A(P2b implies P2a),P2A蕴涵P2(蕴涵是充分条件的意思) ( which in turn implies P2.)

[译者注解

  因此接下来的重点就是论证p2b成立即可, 假设某个提议{m,v}已经被选择了,证明任何编号n>M的提议,其value值都是v

 第二数学归纳法

  设P(n)是一个与自然数有关的命题,如果:

     1°P(1)成立;

     2° 假设P(n)对于所有满足l<k成立,则P(k)成立;

 那么P(n)对所有自然数都成立.

 它的理论依据是最小数原理——自然数集的任一非空子集必有一个最小数

数学归纳法的正确性来自Peano公理

需要证明以下结论: 

    假设 {m, v} 被选择, 证明编号为n的提议的值也是v

注释结束]

  接受 [ 副标题 译者注 ] 

      如何才能满足P2B 呢 [ 译者注: 即让P2B结论成立 ],让我们来考虑如何证明这个提议的值就是v(it holds) 。(译者注: P2B 用 数学形式表达 即)  我们假设某个提议{m, v}被选择(译者注: (数学符号表达里的m 其实就是p2B里的n ) ,然后证明(show)任何编号n>m的提议的值都是v (译者注: 数学符号表达里n>m 就是 p2B 里的编号更高提议)。对n归纳可以简化证明,在额外的假设下(under the additional assumption) -- 编号从m到n-1的每个提议值都是v ( i..j 表示从i到j的所有整数的集合),我们需要证明编号为n的提议的值是v。对于已被选择(chosen)的编号为m的提议,必定存在一个集合C由接受了这个提议的acceptor多数派组成,结合归纳假设(induction assumption),m被选择这意味着(多数派的交集必非空):

      对于一个编号在m到n-1范围内的每个提议都至少集合C中的一个acceptor接受了它,每一个集合C中的acceptor都接受了m..(n-1)中的某一个提议,并且任何acceptor接受的编号从m到n-1的提议的值为v。

    因为任何由多数派组成的集合S都至少包含集合C中的一个成员,我们可以得出结论:如果下面p2c的不变性得到了保持,那么编号为n的提议的值是v:

     P2C. 对于任意的 v 和 n,如果一个编号为 n 且拥有值 v 的提案被提议,则存在一个由大多数接受者组成的集合 S 满足这里其中一个条件:

        a) 集合S中所有acceptor都没有接受过编号小于n的议案,或者

        b)  (如果有接受过) 所有编号小于n并被集合S中的所有acceptor接受的提议中,v是编号最大的提议的值。 (译者注 用数学符号表达即 即  x < n ,   acceptedValue(max(x)) = v )

     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.    

 [译者注:  条件p2C ==推导出==>    条件p2b 详细证明过程 ,见附录 ]

  提议 [ 副标题 译者注 ] 

      我们可以通过保持P2C中的不变量,来满足P2B。为了保持P2C的不变性,准备提出议案的proposer必须知道所有编号小于n的议案中编号最大的那个[译者注: P2C 讲的是接受, 因为先有提交后有 接受, 自然要导出"提交"的结论,用于满足P2C ],如果存在的话,它已经或将要被acceptor的某个多数派批准。获取已经批准的提议是简单的,但是预知将来可能批准的议案是困难的。Proposer并不做预测,而是提出(extract)一个承诺,永不会有这样的接受(such acceptances)。也就是说,proposer要求acceptor不能批准任何编号小于n的议案。这引出了下面提出提议的算法 [译者注: 终于到 提议的 流程了 ] 

 1 proposer选择一个新编号n,向some acceptor集合中的所有成员发送请求,并要求acceptor回应:

    a) 一个永不接受编号小于n的议案的承诺,以及

    b) 在它已经接受的所有编号小于n的提议中,编号最大的提议,如果存在的话。

( 译者注 用数学符合表达即 即 returnV = acceptValueOfMax( acceptedN  ) )

   我把这样的请求称为prepare request with number n。

  2 如果proposer收到了acceptors中多数派的回应,那么它就可以提出提议{n, v},其中 如果有批准过, v=所有回应中编号最高的提议的值,或者 如果acceptor们回应说还没有批准过议案, v= proposer选择的任意值(译者注: 这个值作为proposer提案的值). ( 译者注 用数学符合表达即 即 v = acceptedValueOfMaxN == null? numOfProposer : acceptedValueOfMaxN )

    一个proposer向一个acceptor集合发送提议(不必是回应proposer初始请求的acceptor集合),我们称之为accept请求。我们已经描述了proposer的算法。那么acceptor呢?它(acceptor)可以接收两种来自proposer的请求: prepare请求和accept请求。Acceptor可以忽略任何请求,而不用担心安全性。因此,我们只需要描述它需要回应请求的情况。任何时候它都可以回应prepare请求,它可以回应accept请求,接受提议,当且仅当它承诺过,换句话讲:

   P1A. acceptor可以接受一个编号为n的提议,当它没有回应过一个编号大于n的prepare请求。( 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 ( 译者注: 蕴含 值 充分条件 ,显而易见,  P1A 可以证明出 P1)

    现在我们得到了一个完整的算法,它满足我们要求的安全属性—— 假设议案编号全局唯一。通过一个小的优化就能得到最终算法。

    假设一个acceptor接收到一个编号为n的prepare请求,但是它已经回应了一个编号大于n的prepare请求。于是acceptor就不会接受这个编号为n的prepare请求了,因为它不会接受这个编号为n的提议,所以这个acceptor会忽略(ignore)这个编号为n的prepare请求,它也会忽略包含它已经接受的编号的prepare request.

存储数据结构 [ 译者新增的副标题]

   有了这个优化,acceptor只需要保存它已经批准的最高编号的提议(包括编号和决议),以及它已经回应的最高编号。因为任何情况下,都需要保证P2C的不变性,acceptor必须记住这些信息,即使在失效(fails)并重启之后。注意,proposer可以在任何时候的抛弃一个提议并忘记它——只要它永远不会使用相同的编号来提出另一个提议。

完整决议流程 [ 译者新增的副标题]

   结合proposer和acceptor的行为,我们将把算法可以分为两个阶段来执行。   

阶段1.

      a) Proposer选择一个议案编号n,向acceptor的多数派发送编号为n的prepare请求。

      b) Acceptor:如果接收到的prepare请求的编号n大于它已经回应的任何prepare请求,它就回应此请求

        ---已经接受的编号最高的提议(如果有的话),并承诺(promise)不再回应任何编号小于n的议案;

阶段2.

      a) Proposer:如果收到了多数acceptor对prepare请求(编号为n)的回应,它就向这些acceptor

        发送提议{n, v}的accept请求,其中v是所有回应中编号最高的提议的值,或者是proposer选择的值,

        如果全部回应都还没有值。

      b) Acceptor:如果收到了提议{n, v}的accept请求(proposer promise accept requset),它就批准(accept) [译者注 wiki 上 用了accepted一词 ,用于区别proposer promise accept请求  ] 该议案,除非它已经回应了一个编号大于n的提议。

    译者注, 基于上面的阶段1,阶段2,和本文开头的"选择"的概念, 补充流程图. maxN是回应过的序号, acceptN是接受的序号

 译者注: wiki上的时序图.  竖线上的X代表接受并处理.

wiki上的异常流程时序图 

     Proposer可以提出多个提议(multiple proposals),只要它遵循上面的算法(algorithm),它可以在算法的任何时刻放弃一个提议。(这不会破坏正确性,即使在议案被放弃许久后,提议的request请求或者回应消息才到达目标). 如果其它的proposer已经开始提出更高编号的议案,那么放弃当前的提议很大可能更好。因此,如果acceptor忽略一个prepare或者accept请求(因为已经收到了更高编号的prepare请求),它应该(probably)告知proposer放弃提议, 这是一个性能优化,不影响正确性。

2.3 Learner获知已经被选择的值

  Learner被acceptor通知

   Learner必须找到一个被多数acceptor批准的提议,才能知道一个值(value)被选择了。一个显而易见的算法就是,让每个acceptor在批准提议时通知所有的learner,把proposal发给他们。这可以让learner尽快知道被选择的值[译者注: learner通过大多数判断来确定这个值是否是被选择的],但是它要求每个acceptor通知每个learner——需要的消息个数等于learner数和acceptor数的乘积。

  Learner优化  [ 译者新增的副标题]

    基于非拜占庭失败假设,一个learner可以从另一个learner得知被选择的值。我们可以让acceptor将接受情况回应给一个主(distinguished) Learner,它再把被选择的值通知给其它的learner。这增加了一次额外的消息传递,会比前一种不可靠一些,因为主learner可能会失效,但是要求的消息个数仅是learner数和acceptor数的总和。

    更一般的,acceptors可以通知多个主(distinguished)Learners,每个主learner都能通知其它所有的learners。主learner越多越可靠,但是通信复杂性会增加

   Learner消息丢失和主动获取/重试  [ 译者新增的副标题]

    由于消息丢失,可能没有learner知道选择了一个值。Learner可以向acceptor询问批准的提议,但是由于acceptor的失效(failed),可能难以得知多数派是否接收了一个值。这样,learner只能在新的提议被选择时才能知道the value。如果learner需要知道是否一个值已经被选择了,它可以让proposer根据上面的算法提出一个新的提议译者注: 提出请求就有回应,并且新的提案的决议就是当前选择的决议,这样learner会提到通知】。[译者注:  zk中增加了轮的概念,每一轮 ]

译者注: 
     回答本文最上面的译者提出的问题. 

             问: 决策的结果,存储在哪, accepter挂掉了怎么办?
             答:  决策的本身其实存储在半数的accepter里. 虽然决策超过半数就够了,但是为了可靠性,最好需要超过+1

 2.4 处理流程(progress)

   活锁  [ 译者新增的副标题]

    很容易构造这样一个场景,两个proposer轮流提出一系列编号递增的议案,但是都没有被选择。Proposer p用提议的编号为n1,并结束阶段1; 另外一个proposer q选择了提议编号n2>n1,并结束阶段1。于是p在阶段2的accept请求将被忽略,因为acceptor已经承诺不再批准编号小于n2的议案(详见 提出议案 部分,要求acceper做的工作 )。于是p再回到阶段1并选择了编号n3 > n2,这又导致q第二阶段的accept请求被忽略;这样一直循环下去。[译者注 这就是paxos 活锁]

    为了保证达到最终一致的流程能够结束,必须选择一个主(distinguished) proposer,只有主proposer才能提出提议。如果主proposer和多数acceptor成功通信,并提出一个编号更高的提议,提议将被接受,值会被选择。如果它得知已经有编号更高的议案,它将放弃当前的提议,主proposer最终能选择一个足够大的编号。[译者注 这里的"必须" 其实不必须, 活锁不是必然会发生, 选主可以降低概率,但不必须, 详见 本文"实现"一章中的"leader作用和leader冲突 "一节 所以这个选主算法可以比较简单,可重复. {高阶}更有意思的是用简单选主算法启动paxos算法来真正的选主, 进一步可以确定zk中那个是leader,然后跑后续的paxos算法,变成有主的multi paxos, 哪怕leader挂掉后,还能恢复存储 ]

    如果系统中有足够的组件( proposer,acceptor 和网络 )能正常工作,通过选择一个主proposer ,活性(liveness) [6] 就能得到满足。Fischer、Lynch和Patterson的著名结论[1]表明:选择主proposer的可靠算法必须使用随机(randomness)或者实时(real time)——例如,使用超时机制(timeout)。然而不管选择成功与否,安全性都能得到保证。

    ( 译者注: [1] 即是FLP不可能定理:在异步通信场景,即使只有一个进程失败,也没有任何算法能保证非失败进程达到一致性 )

 2.5 实现

Paxos算法[5]假设了一个某些进程(processes)组成的网络(network)。在其一致性算法中,每个process都同时扮演proposer、acceptor和learner的角色。算法选择一个leader,它就是主proposer和主learner。Paxos一致性算法就是上面描述的那个,请求和响应都用普通消息发送(响应会被打上对应提议的编号,以防止混淆)。使用持久化存储来保证acceptor失效后也能记住必要的信息。Acceptor在发送响应前必须持久化存储该响应。

    接下来就是描述保证任何两个提议的编号都不相同的机制。proposer从互不相交的集合中选择议案编号,因此两个不同的proposer永远不会提出相同编号的议案。每个proposer都持久化保存(in stable storage)它已经提出的编号最高的提议,并使用一个更高的提议编号来开始阶段1。

 3 实现状态机模型

译者注释: 这里的状态机其实是广义上的状态机, 指的是任何有状态的系统. 而不是传统程序员理解的狭义的状态机. 通过下文的文字就能理解 "状态机的状态可能(might)由所有用户的账户余额构成。"

[ 译者注释

有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在面向对象的软件系统中,一个对象无论多么简单或者多么复杂,都必然会经历一个从开始创建到最终消亡的完整过程,这通常被称为对象的生命周期。一般说来,对象在其生命期内是不可能完全孤立的,它必须通过发送消息来影响其它对象,或者通过接受消息来改变自身。在大多数情况下,这些消息都只不过是些简单的、同步的方法调用而已。

在描述有限状态机时,状态、事件、转换和动作是经常会碰到的几个基本概念。

  状态(State)指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件。

 事件(Event)指的是在时间和空间上占有一定位置,并且对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。

 转换(Transition)指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生同时某个特定条件满足时进入第二个状态。

 动作(Action)指的是状态机中可以执行的那些原子操作,所谓原子操作指的是它们在运行的过程中不能被其他消息所中断,必须一直执行下去。

 注释结束]

实现分布式系统的一种简单方式就是使用一些客户端向中心(central)服务器发送命令【就是C/S模式了】。服务器可以看作是根据一定顺序执行客户端命令的确定(deterministic)状态自动机。状态机包含当前状态,它读入input作为一个命令然后产生相应的结果,这样它进入到一个新的状态,这个过程叫做一步(a step)。比如,分布式银行系统的客户端可能是出纳员,状态机的状态可能(might)由所有用户的账户余额构成。取款操作通过执行一条状态机命令来完成:减少一个账户余额,输出新旧余额数(当且仅当余额大于取款值)。

    使用单个中心服务器的实现是不可靠的,当这个机器fail时,服务fail了。因此我们使用一组服务器 [译者 分布式就出现了,CAP中P有了] ,每一个都独立的实现了状态机。因为状态机是确定的(deterministic),如果执行相同的命令序列(same sequence of commands),所有的服务器将会产生同样(the same)的状态序列和输出【非拜占庭模型假设】。客户端发起命令后,任何服务器可以响应并创建输出(output), 客户端可以使用这个输出。

    为了保证所有的服务器执行的是相同的状态机命令序列,我们通过paxos一致性算法执行多个有顺序的相互独立的instance(选择(chose)一个value的过程是一个instance) [ 译者注 后人优化演进成了 multi Paxos,先选主,只有一个proposal,一个accepter,然后主来执行和同步指令,确保各个learner时序一致],第i个实例(instance)选择的值(value)就是序列的第i个状态机命令。在算法的每个实例中,每个server都扮演所有的角色(proposer、acceptor和learner)。现在,假设服务器的集合固定(fixed),所有的paxos实例都使用相同集合的角色(agents)。

leader作用和leader冲突 [ 译者 新增的二级目录 ]

    正常操作下,一个服务器被选择为leader,它就是所有的paxos实例(instance)的主proposer( 唯一能够提出提议的[译者注98, 本文论文这里提到的leader尽量唯一但不强求,  提交议案时需要进行两阶段的提交. ] [与现在的zab/raft区别是, zab/raft的leader都是先选主且必须唯一, 且后续提交时仅一阶段即可] )。

    客户端向leader [ 译者注99: 即主(distinguished) proposer ]发送命令,leader 决定每个命令的时序。如果leader决定第135序号 [译者注100: 这里的序号是状态机命令序号 和 一次paxos过程中的不同proposer的全局序号是两个层次上的概念]的命令应该是某个客户端的命令A,它将试图把该命令选择为第135个pasox实例的值,通常都能成功。

     有可能因为失效(failures)而失败(fail),或者另外的一个服务器相信它也是leader[译者注101: 见上面译者注里与现在zab/raft的区别解答 ],那个服务器认为第135个instance应该是别的值。但是一致性算法保证最多只能有一个命令能被选择成为第135个paxos instance的值。[ 译者注102: 同一个状态机"序号"并发提交, 只会选择一个.  这里面用状态机"序号"来确定一个paxos过程, "状态机序号"和前面paxos算法的序号的区别详见译者注100  ]

   Paxos一致性算法的效率(efficiency)的关键在于,提议的value直到阶段2才会被选择。在算法的阶段1结束后,或者提决的值已经确定(determined)了,或者proposer可以提出任何提议。

    我将描述正常运行下(during normal operation) Paxos状态机实现(implementation)的执行情况。  

异常情况之leader失效 [ 译者 新增的二级目录 ]

    接下来,再讨论可能的异常情况(what can go wrong)。我会考虑前一个leader失效(failed)、新的leader刚被选择,这时会发生什么情况(系统启动是个特例(specail case),这时还没有提出提议)。

  提案同步[ 译者 新增的三级目录 ]

     新的leader在paxos一致性算法的所有实例中也是learner,应该知道大部分(most)已经被选择的命令(value)。假设它知道1号至134号命令,第138号命令和第139号命令[ 译者注: 没有 135,136,137号提案结果 ]——也就是算法实例1–134、138和139选择的值(后面将看到命令序列中的间隔[gap]是如何引起[arise]的)。[译者新起一段]

     然后(新)leader执行paxos实例135-137的阶段1和所有大于139的paxos实例(后面会描述如何做到这一点)。假设这些执行的结果将决定paxos实例135号和140号提议的值,但是让其它paxos实例的提议的值未受到限制(unconstrained)。然后(新)leader为paxos实例135号和140号执行阶段2,并选择了第135号和140号个命令 [ 译者注: 这个过程就是新leader同步老leader提案结果的过程. 详见"2.3 Learner获知已经被选择的值" 这大段最后一句话及译者注解]。

   提案跳号补全/ 重新提交议案 [ 译者 新增的三级目录 ]

    Leader和其它所有(知道leader知道的所有命令)的服务器,现在可以执行命令1-135。但是不能执行命令138-140(虽然它们也知道),因为第136号和137号命令还没有被选择,[ 译者注: 两个命令只要不存在互斥, 该数据库可以并发的进行多个paxos实例, 所以会有跳号的现象, 138已经被选择,而136号还没有被选择出来]。Leader也可以把客户端接下来提出的两个命令选为命令136号和137。但是我们通过提议一个特殊的"no-op"(不会改变状态机的状态)命令作为136和137,立即填充命令的空缺(fill the gap),(通过执行paxos实例136和137的阶段2来做到这一点)[译者注: 详见"2.3 Learner获知已经被选择的值" 最后一句话及译者注解]。一旦这些no-op命令被选择,命令138-140就能被执行了。[译者注150: 还有一种方式避免串行,就是模仿mvcc的事务的行锁/间隙锁机制. 加同样锁的串行,其他的可以随意 ]

    现在命令1-140已经被选择了。Leader还已经为所有大于140的paxos实例完成了一致性算法的阶段1的操作【注解*】,并可以自由地在这些paxos实例在阶段2中提议任何值。它把客户端的请求下一条命令编号为141,提议它作为实例141的阶段2的值。再把它收到的下一条客户端命令提议为142命令,然后继续下去(and so on)。  

  跳号间隔产生原因[ 译者 新增的三级目录 ]

    在知道提出的命令141被选择之前,Leader可以提出命令142。它提议命令141的过程中它发送的所有的消息可能都丢失了,还可能在其他任何服务器知道(leader提议命令141的)值之前,命令142已经被选择了。第141个实例, 当leader无法收到(fail to receive)响应,以至于无法进入阶段2,它将重传这些消息。如果一切顺利,它提出的命令将被选择。然而,它(141)也可能会先失败(fails),这样在已经选择的命令序列中留下了一个间隔。一般来说,假设leader可以提前α个命令——也就是说,在命令1到i被选择之后,它就可以同时提出命令i+1到 i+α。那么最多可以产生由α-1个命令组成的间隔(gap)【容易理解,前面的α-1个命令都失败了】。

     新选出的leader要为paxos算法的许多个实例执行阶段1——在上面的场景中,是实例135-137,然后正常执行139之后的所有实例。对所有实例使用相同的提议编号,它可以通过向其他的服务器发送一条短的合理的消息来做到这一点(a single reasonably short message)。在阶段1,仅当acceptor已经收到了一个来自其它proposer的阶段2的消息时,除了简单的OK,它的回应会包含额外的消息,(在上述场景中,仅实例135和140是这样的,因为135和140的阶段1由原来的leader已经完成了)。因此,一般情况下服务器(作为acceptor)可以为所有实例(instance)回应一条(single)合理的短消息[译者注: 合理的短消息里包含了这是那一轮实例信息即可,有点multi paxos的味道,但作者并没有说太多.]。这些许多个实例(instance)的阶段1并不会产生(pose)任何问题。 

     因为leader的失效和由此引发的选举应该是小概率事件,执行一条状态机命令的实际(effective cost)开销 ——达到对命令/值的一致性—— 仅为运行这个一致性算法的阶段2的开销(cost of executing only phase 2 of the consensus algorithm.)。能够被展示出来,为了达成一致,在允许失效的情况下(in the presence of faults),在所有的同类(一致性)算法中, paxos一致性算法的阶段2具有最小可能的开销[2]。因此paxos算法在本质上是最佳的(optimal)。[ 译者注: "仅运行阶段2"有点 multi paxos的味道, wiki上的multi paxos时序图 如下: ] [ 事实上后来演进的算法是能够做到 leader确保仅一台 , 即当选leader要超过半数同意, 且二阶段仅对leader答复且要超过半数, zab/raft 均是如此 ]     

   无leader和多leader情况安全性说明 [译者新增二标题]   

  对系统正常(normal)操作的讨论假设一直存在单个(single)leader,除去(except)在当前leader失效和选举新leader之间的一小段时间。在异常环境中,leader选择可能失败。如果没有服务器被选为leader,那么没有新命令会被提议。如果多个服务器都认为自己是leader,在同一个算法实例中,它们将都能提出提议,这可能会导致所有的提议都不能被选择。然而,安全属性是保持的(preserved) —— 两个不同的服务器永远不会在第i个状态机命令的值上有分歧(disagree)。选择单一的leader只是为了确保流程(progress)(不活锁)。

    如果服务器的集合会变动 [ 译者注: 前面文章中指定了不会变fixed ],那么必须有某种方法能够确定哪些服务器执行的是算法的哪个实例。最简单的做法就是让状态机自己做到。当前的服务器集合可以作为状态的一部分,并能被普通的状态机命令修改。通过(执行i+α命令的)服务器集合由第i个状态机命令执行后的状态确定,我们可以让leader提前同时执行α个命令。这允许(permit)了一个任意复杂算法的简单实现。

 [注释

http://en.wikipedia.org/wiki/Paxos_algorithm

http://www.wikilib.com/wiki/Paxos%E7%AE%97%E6%B3%95

【还有两个重要的问题就是如何选举leader,以及server间数据的同步,可以参看zookeeper的实现】

 ]

参考文献

[1] Michael J. Fischer, Nancy Lynch, and Michael S. Paterson. Impossibility of distributed consensus with one faulty process. Journal of the ACM, 32(2):374–382, April 1985.

[2] Idit Keidar and Sergio Rajsbaum. On the cost of fault-tolerant consensus when there are no faults—a tutorial. TechnicalReport MIT-LCS-TR-821, Laboratory for Computer Science, Massachusetts Institute Technology, Cambridge, MA, 02139, May 2001. also published in SIGACT News 32(2) (June 2001).

[3] Leslie Lamport. The implementation of reliable distributed multiprocess systems. Computer Networks, 2:95–114, 1978.

[4] Leslie Lamport. Time, clocks, and the ordering of events in a distributed system. Communications of the ACM, 21(7):558–565, July 1978.

[5] Leslie Lamport. The part-time parliament. ACM Transactions on Computer Systems, 16(2):133–169, May 1998.

附录

译者注释,证明p2c ⇒p2b 的过程

我们再来看p2c,实际上p2c规定了每个proposer如何产生一个提案:对于产生的每个提案[Mn,Vn] 需要满足如下条件:

存在一个由超过半数的Acceptor组成的集合S:

        要么S中没有Acceptor批准过编号小于Mn的任何提案

        要么S中的所有Acceptor批准的所有编号小于Mn的提案中,编号最大的那个提案的value值为Vn

    当每个proposer都按照这个规则来产生提案时,就可以保证满足p2b了,接下来我们就使用第二数学归纳法来证明p2b

    首选假设提案[M0,V0]被选定了,设比该提案编号大的提案为[Mn,Vn],我们需要证明的就是在p2c的前提下,对于所有的[Mn,Vn],必有Vn=v0

   1. 当Mn=M0+1时,如果有这样一编号为M0+1的提案,那么根据p2c,存在一个多数派集合S. 根据S的情况提出了Vn。设S0是批准M0的多数派的acceptors构成的集合,由多数派的性质S0和S的交集非空,所以S中的所有acceptors不能都没批准过编号小于M0+1的提案。 设S中所有Acceptor批准的所有编号小于M0+1的提案中,编号最大的那个提案的编号是X,显然有X<=M0, 设S0和S的交集共有一个acceptor Z,  因为Z 属于S0,所以Z批准的最大编号Y=M0,而Z又属于S,得出X要大于等于Z的最大编号Y即   M0=Y<=X<=M0, 所以 X=M0,

      所以此时Vn 的值是M0的值V0

      以上是数学归纳的第一步,验证初值成立

  2。 根据假设,编号在M0+1到Mn-1区间内的所有提案的value值为V0,需要证明的是编号为Mn的提案的value值

也为V0, 根据p2c,首先同样一定存在一个Acceptor的子集S,且S中的Acceptor已经批准了小于Mn的提案,那么编号为Mn的提案的Value值只能是这个多数集S中编号小于Mn但为最大编号的那个提案的值. 如果这个最大编号落在M0+1到Mn-1区间内,那么Value的值肯定是V0, 如果不落在M0+1到Mn-1的区间内,那么它的编号不可能比M0再小了,肯定就是M0,因为S也肯定会与批准[M0,V0]这个提案的Acceptor集合S0有交集。 而如果编号是M0,那么它的Value值也是V0,由此得证结论

注释结束]

 

翻译过程参考资料

辅助翻译工具

  from 《Paxos Made Simple》中文翻译:Paxos 如此简单  这里面有对应到原文上.

  • 有道词典

  1. 让人摸不着头脑:原文中的词是“Greek”,我个人猜想这里其实是一语双关,一个意思是指 Leslie Lamport 第一次阐述 Paxos 算法的论文《The Part-Time Parliament》里用了古希腊的故事情节来阐述算法思路,另一个意思表达令人摸不着头脑,可参考有道词典双语例句:
    Reporter: The new version will promote "The Painted" as "Eastern magic of the new", is not it a bit too exaggerated and Greek?Positioning yourself how this movie?
    记者:宣传方将新版《画皮》定位为“东方新魔幻”,是不是有点儿太夸张并且令人摸不着头脑?你自己怎么定位这部电影?。
     
  2. synod 算法:论文作者在《The Part-Time Parliament》中对其算法的命名为 synod。 
  3. 来自维基百科的“状态机复制”词条:多个相同状态机的拷贝,从同样的“初始”状态开始,经历了相同的输入序列后,会达到同样的状态,并且输出同样的结果。 
  4. 原文中为单词values,翻译过程中结合上下文理解,认为加上“可能不同的”会更贴合情境。 
  5. 原文内容为“A process never learns that a value has been chosen unless it actually has been.” 
  6. 关于安全性属性以及活性属性,可查阅“本文参考资料”一节的“Safety & Liveness Properties” 
  7. 原文单词“corrupted”,直译应为“损坏”,但是这里结合个人理解,译作“篡改”更贴切,意为不会发生拜占庭式的问题 
  8. 由于所谓的大多数等于 N/2+1,所以如果有一个接受者故障,可能导致两边提议都只能得到 N/2 票,都无法行成大多数。 
  9. 一种常见且可行的方案是使用时间戳+机器ID的形式,但是实际上论文中并没有对提案编号的生成做具体的规定,只要保证编号递增且唯一即可,所以实际的实现中可以有多种多样的实现方式 
  10. 论文作者采用了数学上的第二归纳法,亦称“强归纳法” 
  11. 结合后面的推理结论,这里是一个闭区间的标记法,即i..j对应数学记法 [i, j]。 
  12. 至少接受了 m 号提案,所以这个结论是成立的 
  13. 记得一个“提案”始终意味着:一个编号加上一个提案的值。 
  14. 回想基本的 Paxos 的两个阶段中的阶段一,只要当前实例中,还没有任何提案被接受者接受过,则提议者可以提出任意值。这种情况下,意味着原来旧的 Leader 还没有来得及开始实例 136 和 137 的阶段二。 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值