分布式系统原理 之8 Paxos协议

Paxos协议是一种实现分布式系统强一致性的协议,通过节点间的投票达成共识。协议涉及Proposer、Acceptor和Learner三种角色,其中Proposer提出议案,Acceptor批准,Learner学习批准的值。流程包括准备阶段和批准阶段,确保批准的值一旦确定无法改变。在异常情况下,如节点宕机或网络分区,Paxos仍能保持工作,防止死锁和活锁。
摘要由CSDN通过智能技术生成

分布式系统原理

Paxos协议

1. 简介

Paxos 协议是少数在工程实践中证实的强一致性、高可用的去中心化分布式协议。

Paxos 协议的流程较为复杂,但其基本思想却不难理解,类似于人类社会的投票过程。Paxos 协议中,有一组完全对等的参与节点(称为 accpetor),这组节点各自就某一事件做出决议,如果某个决议获得了超过半数节点的同意则生效。Paxos 协议中只要有超过一半的节点正常,就可以工作,能很好对抗宕机、网络分化等异常情况。

2. 协议描述

2.1 节点角色

Paxos 协议中,有三类节点:

Proposer:提案者。Proposer 可以有多个,Proposer 提出议案(value)。所谓 value,在工程中可以是任何操作,例如“修改某个变量的值为某个值”、“设置当前 primary 为某个节点”等等。Paxos协议中统一将这些操作抽象为 value。不同的 Proposer 可以提出不同的甚至矛盾的 value,例如某个Proposer 提议“将变量 X 设置为 1”,另一个 Proposer 提议“将变量 X 设置为 2”,但对同一轮 Paxos过程,最多只有一个 value 被批准。

Acceptor:批准者。Acceptor 有 N 个,Proposer 提出的 value 必须获得超过半数(N/2+1)的 Acceptor批准后才能通过。Acceptor 之间完全对等独立。

Learner:学习者。Learner 学习被批准的 value。所谓学习就是通过读取各个 Proposer 对 value的选择结果,如果某个 value 被超过半数 Proposer 通过,则 Learner 学习到了这个 value。回忆(2.4 )不难理解,这里类似 Quorum 机制,某个 value 需要获得 W=N/2 + 1 的 Acceptor 批准,从而学习者需要至少读取 N/2+1 个 Accpetor,至多读取 N 个 Acceptor 的结果后,能学习到一个通过的 value。

上述三类角色只是逻辑上的划分,实践中一个节点可以同时充当这三类角色。

2.2 流程描述

Paxos 协议一轮一轮的进行,每轮都有一个编号。每轮 Paxos 协议可能会批准一个 value,也可能无法批准一个 value。如果某一轮 Paxos 协议批准了某个 value,则以后各轮 Paxos 只能批准这个value。++上述各轮协议流程组成了一个 Paxos 协议实例,即一次 Paxos 协议实例只能批准一个 value,这也是 Paxos 协议强一致性的重要体现。++

每轮 Paxos 协议分为阶段,++准备阶段和批准阶段++,在这两个阶段 Proposer 和 Acceptor 有各自的处理流程。

流程 2.8.1:Proposer 的流程
(准备阶段)
1. 向所有的 Acceptor 发送消息“Prepare(b)”;  这里 b 是 Paxos的轮数,每轮递增
2. 如果收到任何一个 Acceptor 发送的消息“Reject(B)”,则对于这个 Proposer 而言本轮 Paxos 失败,将轮数 b 设置为 B+1 后重新步骤 1;

(批准阶段,根据收到的 Acceptor 的消息作出不同选择)
3. 如果接收到的 Acceptor 的“Promise(b, v_i)”消息达到 N/2+1 个(N 为 Acceptor 总数,除法取整,下同);v_i 表示 Acceptor 最近一次在 i 轮批准过 value v。
    3.1 如果收到的“Promise(b, v)”消息中,v 都为空,Proposer 选择一个 value v,向所有 Acceptor广播 Accept(b, v);
    3.2 否则,在所有收到的“Promise(b, v _ i)”消息中,选择 i 最大的 value v,向所有 Acceptor 广播消息 Accept(b,v);
4. 如果收到 Nack(B),将轮数 b 设置为 B+1 后重新步骤 1;
流程 2.8.2:Accpetor 流程
(准备阶段)
1. 接受某个 Propeser 的消息 Prepare(b)。参数 B 是该 Acceptor 收到的最大 Paxos 轮数编号;V 是 Acceptor 批准的 value,可以为空
    1.1 如果 b>B,回复 Promise(b, V_B),设置 B=b;表示保证不再接受编号小于 b 的提案。
    1.2 否则,回复 Reject(B)。

(批准阶段)
2. 接收 Accept(b, v),
    2.1 如果 b < B, 回复 Nack(B),暗示 proposer 有一个更大编号的提案被这个 Acceptor 接收了。
    2.2 否则设置 V=v。表示这个 Acceptor 批准的 Value 是 v。广播 Accepted 消息。

3. 实例

3.1 基本例子

基本例子里有 5 个 Acceptor,1 个 Proposer,不存在任何网络、宕机异常。我们着重考察各个Accpetor 上变量 B 和变量 V 的变化,及 Proposer 上变量 b 的变化。

  1. 初始状态

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B00000
    VNULLNULLNULLNULLNULL
    -Proposer 1
    b1
  2. Proposer 向所有 Accpetor 发送“Prepare(1)”,所有 Acceptor 正确处理,并回复 Promise(1, NULL)

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B11111
    VNULLNULLNULLNULLNULL
    -Proposer 1
    b1
  3. Proposer 收到 5 个 Promise(1, NULL),满足多余半数的 Promise 的 value 为空,此时发送Accept(1, v1 ),其中 v1 是 Proposer 选择的 Value。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B11111
    Vv1v1v1v1v1
  4. 此时,v 1 被超过半数的 Acceptor 批准,v1 即是本次 Paxos 协议实例批准的 Value。如果 Learner学习 value,学到的只能是 v1

3.2 批准的 Value 无法改变

在同一个Paxos实例中,批准的Value是无法改变的,即使后续Proposer以更高的序号发起Paxos协议也无法改变 value。

  1. 例如,某次 Paxos 协议运行后,Acceptor 的状态是:

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B33322
    Vv1v1v1NULLNULL

    5 个 Acceptor 中,有 3 个已经在第三轮 Paxos 协议批准了 v 1 作为 value。其他两个 Acceptor 的 V为空,这可能是因为 Proposer 与这两个 Acceptor 的网络中断或者这两个 Acceptor 宕机造成的。

  2. 此时,即使有新的 Proposer 发起协议,也无法改变结果。假设 Proposer 发送“prepare(4)消 息”,由于 4 大于所有的 Accpetor 的 B 值,所有收到 prepare 消息的 Acceptor 回复 promise 消息。但 前三个 Acceptor 只能回复 promise(4, v 1 _3),后两个 Acceptor 回复 promise(4, NULL)。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B44444
    Vv1v1v1NULLNULL
  3. 此时,Proposer 可能收到若干个 Acceptor 发送的 promise 消息,没有收到的 promise 消息可能是网络异常造成的。无论如何,Proposer 要收到至少 3 个 Acceptor 的 promise 消息后才满足协议中大于半数的约束,才能发送accpet消息。这3个promise消息中,至少有1个消息是promise(4, v 1 _3),至多 3 个消息都是 promise(4,v 1 _3)。另一方面,Proposer 始终不可能收到 3 个 promise(4, NULL)消息,最多收到 2 个。综上,按协议流程,Proposer 发送的 accept 消息只能是“accept(4, v 1 )”而不能自由选择 value。

    无论这个 accept 消息是否被各个 Acceptor 接收到,都无法改变 v 1 是被批准的 value 这一事实。即从全局看,有且只有 v 1 是满足超过多数 Acceptor 批准的 value。例如,假设 accept(4, v 1 )消息被Acceptor 1、Acceptor2、Acceptor4 收到,那么状态变为:

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B44444
    Vv1v1v1v1NULL

    从这个例子我们可以看到一旦一个 value 被批准,此后永远只能批准这个 value。

3.3 一种不可能出现的状态

Paxos 协议的核心就在与“批准的 value 无法改变”,这也是整个协议正确性的基础,为了更好的理解后续对 Paxos 协议的证明。这里再看一种看似可能,实际违反协议的状态,这种状态也是后续反证法证明协议时的一种错误状态。

-Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
B11111
Vv1v1v1v2v2

上述状态中,3 个轮次为 1 的 Acceptor 的 value 为 v 1 ,2 个轮次更高的 Acceptor 的 value 为 v 1 。此时被批准的 value 是 v 1 。

假设此时发生新的一轮 b=3 的 Paxos 过程,Proposer 有可能收到 Acceptor 3、4、5 发出的 3 个promise 消息分别为“promise(1, v 1 _1)”,“promise(2, v 2 _2)” “promise(2, v 2 _2)”。按协议,proposer选择 value 编号最大的 promise 消息,即 v 2 _2 的 promise 消息,发送消息“Accept(3, v 2 )”,从而使得最终的批准的 value 成为 v 2 。就使得批准的 value 从 v 1 变成了 v 2 。

上述假设看似正确,其实不可能发生。这是因为本节中给出的初始状态就是不可能出现的。这是因为,要到达成上述状态,发起 prepare(2)消息的 proposer 一定成功的向 Acceptor 4、Acceptor 5发送了 accept(2, v 2 )。但发送 accept(2, v 2 )的前提只能是 proposer 收到了 3 个“promise(2, NULL)”消息。 然而,从状态我们知道,在 b=1 的那轮 Paxos 协议里,已经有 3 个 Acceptor 批准了 v 1 ,这 3 个Acceptor 在 b=2 时发出的消息只能是 promise(2,v 1_ 1),从而造成 proposer 不可能收到 3 个“promise(2,NULL)”,至多只能收到 2 个“promise(2, NULL)”。另外,只要 proposer 收到一个“promise(2,v 1 _1)”,其发送的 accept 消息只能是 accept(2, v 1 )。

从这个例子我们看到 Prepare 流程中的第 3 步是协议中最为关键的一步,它的存在严格约束了“批准的 value 无法改变”这一事实。在后续协议推导中我们将看到这一步是如何被设计出来的。

3.4 节点异常

这里给一个较为复杂的异常状态下Paxos 运行实例。本例子中有 5 个Acceptor和 2 个 Proposer。

  1. Proposer 1 发起第一轮 Paxos 协议,然而由于异常,只有 2 个 Acceptor 收到了 prepare(1)消息。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B11000
    VNULLNULLNULLNULLNULL
  2. Proposer 1 只收到 2 个 promise 消息,无法发起 accept 消息;此时,Proposer 2 发起第二轮 Paxos协议,由于异常,只有 Acceptor 1、3、4 处理了 prepare 消息,并发送 promise(2, NULL)消息

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B21220
    VNULLNULLNULLNULLNULL
  3. Proposer 2 收到了 Acceptor 1、3、4 的 promise(2, NULL) 消息,满足协议超过半数的要求,选择了 value 为 v 1 ,广播了 accept(2, v 1 )的消息。由于异常,只有 Accptor 3、4 处理了这个消息。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B21220
    VNULLNULLv1v1NULL
  4. Proposer 1 以b=3发起新一轮的Paxos协议,由于异常,只有Acceptor 1、2、3、5处理了prepare(3)消息。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B33323
    VNULLNULLv1v1NULL
  5. 由于异常,Proposer 1 只收到 Acceptor1、2、5 的 promise(3, NULL)的消息,符合协议要求,Proposer 1 选择 value 为 v 2 ,广播 accept(3, v 2 )消息。由于异常,这个消息只被 Acceptor 1、2 处理。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B33323
    Vv2v2v1v1NULL

    当目前为止,没有任何value被超过半数的Acceptor批准,所以Paxos协议尚没有批准任何value。然而由于没有 3 个 NULL 的 Acceptor,此时能被批准的 value 只能是 v 1 或者 v 2 其中之一。

  6. 此时 Proposer 1 以 b=4 发起新的一轮 Paxos 协议,所有的 Acceptor 都处理了 prepare(4)消息。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B44444
    Vv2v2v1v1NULL
  7. 由于异常,Proposer 1 只收到了 Acceptor3 的 promise(4, v 1 _3)消息、Acceptor4 的 promise(4,v 1 _2)、Acceptor5 的 promise(4, NULL)消息,按协议要求,只能广播 accept(4, v 1 )消息。假设 Acceptor2、3、4 收到了 accept(4, v 1 )消息。由于批准 v 1 的 Acceptor 超过半数,最终批准的 value 为 v 1 。

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B44444
    Vv2v1v1v1NULL
4. 竞争及活锁

从前面的例子不难看出,Paxos 协议的过程类似于“占坑”,哪个 value 把超过半数的“坑”(Acceptor)占住了,哪个 value 就得到批准了。

这个过程也类似于单机系统并行系统的加锁过程。假如有这么单机系统:系统内有 5 个锁,有多个线程执行,每个线程需要获得 5 个锁中的任意 3 个才能执行后续操作,操作完成后释放占用的锁。我们知道,上述单机系统中一定会发生“死锁”。例如,3 个线程并发,第一个线程获得 2 个锁,第二个线程获得 2 个锁,第三个线程获得 1 个锁。此时任何一个线程都无法获得 3 个锁,也不会主动释放自己占用的锁,从而造成系统死锁。

但在 Paxos 协议过程中,虽然也存在着并发竞争,不会出现上述死锁。这是因为,Paxos 协议引入了轮数的概念,高轮数的 paxos 提案可以抢占低轮数的 paxos 提案。从而避免了死锁的发生。然而这种设计却引入了“活锁”的可能,即 Proposer 相互不断以更高的轮数提出议案,使得每轮 Paxos过程都无法最终完成,从而无法批准任何一个 value。

  1. Proposer 1 以 b=1 提起议案,发送 prepare(1)消息,各 Acceptor 都正确处理,回应 promise(1,NULL)

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B11111
    VNULLNULLNULLNULLNULL
  2. Proposer 2 以 b=2 提起议案,发送 prepare(2)消息,各 Acceptor 都正确处理,回应 promise(2,NULL)

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B22222
    VNULLNULLNULLNULLNULL
  3. Proposer 1 收到 5 个 promise(1, NULL)消息,选择 value 为 v 1 发送 accept(1, v 1 )消息,然而这个消息被所有的 Acceptor 拒绝,收到 5 个 Nack(2)消息。

  4. Proposer 1 以 b=3 提起议案,发送 prepare(3)消息,各 Acceptor 都正确处理,回应 promise(3,NULL)

    -Acceptor 1Acceptor 2Acceptor 3Acceptor 4Acceptor 5
    B33333
    VNULLNULLNULLNULLNULL
  5. Proposer 2 收到 5 个 promise(2, NULL)消息,选择 value 为 v 2 发送 accept(2, v 2 )消息,然而这个消息被所有的 Acceptor 拒绝,收到 5 个 Nack(3)消息。

上述过程交替进行,则永远无法批准一个 value,从而形成 Paxos 协议活锁。Paxos 协议活锁问题也是这个协议的主要问题。

参考:《分布式系统原理介绍》 - 刘杰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值