Multi-Paxos

https://liu-jianhao.github.io/2019/05/paxosmulti-paxos%E8%AF%A6%E8%A7%A3/


原始的Paxos算法(Basic Paxos)只能对一个值形成决议,决议的形成至少需要两次网络来回(在prepare阶段需要广播到所有节点,这可能造成O(N^2)的灾难性后果),在高并发情况下可能需要更多的网络来回,极端情况下甚至可能形成活锁。如果想连续确定多个值,Basic Paxos搞不定了。因此Basic Paxos几乎只是用来做理论研究,并不直接应用在实际工程中。

实际应用中几乎都需要连续确定多个值,而且希望能有更高的效率。Multi-Paxos正是为解决此问题而提出。Multi-Paxos基于Basic Paxos做了两点改进:

  1. 针对每一个要确定的值(log entry),运行一次Paxos算法实例(Instance),形成决议。每一个Paxos实例使用唯一的Instance ID标识。

  2. 在所有Proposers中选举一个Leader,由Leader唯一地提交Proposal给Acceptors进行表决。这样没有Proposer竞争,解决了活锁问题,而且消灭了prepare环节。

  • 原理:

Multi-Paxos通过改变Prepare阶段的作用范围至后面Leader提交的所有实例,执行一次Basic Paxos实例来选举出一个Leader,从而使得Leader的连续提交只需要执行一次Prepare阶段,后续只需要执行Accept阶段,将两阶段变为一阶段,提高了效率。为了区分连续提交的多个实例,每个实例使用一个Instance ID标识,Instance ID由Leader本地递增生成即可。

Multi-Paxos允许有多个自认为是Leader的节点并发提交Proposal而不影响其安全性,这样的场景即退化为Basic Paxos。


select log entry

  1. entry的jmp按序找出没有被确认的块/空缺的块(以S1为例,没有裱框的3457都是,逐步填充34直到找到自己的归宿5没有人AC<冲突>)
  2. 跑Basic Paxos来propose value
  3. (继续第一步)或者(选择jmp然后结束)
image.png

leader election

  1. server选择最高ID作为leader
  2. 每个server发送心跳包给每个其他server每隔 T ms()
  3. 如果2T ms里没有接收到更高ID的心跳包:
    (1)从客户端接收请求
    (2)既做 proposer 也做 acceptor
  4. 如果不是leader:
    (1)拒绝client的包(重定向给leader,我也不是谦虚)
    (2)只做acceptor
  • 实际上大多数系统都不会采用这种选举方式,它们会采用基于 租约 的方式(lease based approach)
image.png

减少prepare

  1. 第一个问题是阻止所有的提议,我们可以通过改变提议序号的含义来解决这个问题, 将提议序号全局化,代表完整的日志 ,而不是为每个日志记录都保留独立的提议序号。这么做要求我们在完成一轮准备请求后,当然我们知道这样做会锁住整个日志,所以后续的日志记录不需要其他的准备请求。

  2. 第二个问题有点讨巧。因为在不同接受者的不同日志记录里有很多接受值,为了处理这些值,我们扩展了准备请求的返回信息。和之前一样,准备请求仍然返回 acceptor 所接受的最高 ID 的提议,它只对当前记录这么做,不过除了这个, acceptor 会查看当前请求的后续日志记录,如果后续的日志里没有接受值 ,它还会返回这些记录的标志位 noMoreAccepted 。

  3. 使用了这种领导者选举的机制,领导者会达到一个状态,每个 acceptor 都返回 noMoreAccepted ,领导者知道所有它已接受的记录。所以一旦达到这个状态,对于单个 acceptor 我们不需要再向这些 acceptor 发送准备请求,因为它们已经知道了日志的状态。

  4. 不仅如此,一旦从集群大多数 acceptor 那获得 noMoreAccepted 返回值 ,我们就 不需要发送准备的 RPC 请求 。也就是说, leader 可以简单地发送 Accept 请求,只需要一轮的 RPC 请求。这个过程会一直执行下去,唯一能改变的就是有其他的服务器被选举成了 leader ,当前 leader 的 Accept 请求会被拒绝,整个过程会重新开始。

  • 为什么要prepare?

(1)阻止其他old请求跟我们new的竞争
(2)找出被chosen的值(如果有),要不然就用自己的

信息不对称问题

(1)log entry 没有被完整复制(majority only)
(2)只有proposer知道entry什么时候被选中

无法知道什么value被chosen不知道该不该应用到 state machine

  • Solution1(leader后台广播同步entry到所有节点):坚持retrying直到所有acceptor回复收到entry,这步可以放在后台执行不影响其他工作

  • Solution2(标记被选中的entry):如果它发现一个entry已经被chosen,Acceptor设置proposal ID为+∞,从而保证它无法被窜改,因为它是最大值。每个server保留一个firstUnchosenIndex标记未被chosen的entry所在位置。

image.png
  • Solution3(Piggybagging P广播Accept RPC给A们):proposer夹带它的firstUnchosenIndex到给Accepter的RPC中,acceptor的entry chosen原则如下:
    (1)i < request.firstUnchosenIndex
    (2)acceptedProposal[i]==request.proposal
index=8所以把proposal放第八个位置,Proposer(leader)视角

我们知道它已经被 proposer 选定,但是它可能被另外一个值所取代。所以还需要多做一步。

  • Solution4(leader 的旧日志项 <老早以前未AC都可能失效了,比如第4个位置的2.5>):

(1)Acceptor 返回它的 fristUnchosenIndex 在 Accept回答中
(2) 如果 proposer 的 firstUnchosenIndex > acceptor回答的 firstUnchosenIndex(说明acceptor 中的某些状态不确定),proposer就会在后台发送 Success RPC
(3)Success(index, v):通知 acceptor 该日志项被选中了

acceptedValue[index] = v
acceptedProposal[index] = 无穷大
return firstUnchosenIndex

如果需要(可能存在多个不确定的状态),Proposer 发送额外的Success RPC(出现不止一个2.5)

image.png

客户端协议(leader挂了怎么办,不认识leader怎么办)

(1)发送命令给 leader

  • 如果不知道哪个是 leader,随便选择一个服务器
  • 如果该服务器不是 leader,重定向给 leader

(2)leader 直到日志项被选中并且被 leader 的状态机执行才回应客户

(3)如果请求超时(leader挂了)

  • 客户重新发送命令给其他服务器(重新选举?)
  • 最终重定向给新 leader
  • 给新 leader 重发请求
image.png

命令反复执行的问题(命令对应唯一id)

(1)如果 laeder 在执行完命令但在回应之前挂掉

  • 该命令不能执行两次

(2)解决:客户端需要为每条命令提供一个唯一的 id

  • 服务器会将该 id 存入日志项中
  • 状态机会记录每个客户最近执行的命令,即 最高 id 序号
  • 在执行命令之前,状态机会检查该命令如否被执行过,如果执行过:
    • 忽略
    • 返回之前执行的结果

(3)结果(客户端不崩没事,崩了可能会有没执行的问题):只要客户端不崩溃,就能获得 exactly once 的保证,每个客户端命令仅被集群执行一次。如果客户端出现崩溃,就处于 at most once 的情况,也就是说客户端的命令可能执行,也可能没有执行。但是如果客户端是活着的,这些命令只会执行一次

配置变更

image.png

系统配置:

  1. 每台设备ID,IP
  2. 决定majority的大小

配置更新的原因:

  1. 替换失效的机器
  2. 扩容
  • 一种扩容后导致的配置问题:
左边三台老机器,右边两台新加的机器。实际左边两个根据旧配置达成一致而且占major,右边三个根据新配置达成一致而且占major,出现一致性bug
  • lamport大佬的建议:
  1. 配置被按照entry存储
  2. 跟别的log entry一样复制
  3. 选用哪个配置由a步之前的配置决定,具体如下:(以a=3为例)
第4步选择3步前(第一步)更新的配置,第6步选择第三步更新的配置

a的大小会影响entry执行的效率以及配置更新的速度。a如果很大,那么可以在a范围内不受限制地确认entry并执行,但是这样一来配置更新就得等a个entry执行完,更新速度放缓。如果a很小,配置更新的速度就很快,但是因为a受限,一但网络延迟影响前面的执行就会拖慢执行的速度。

你可能感兴趣的:(Multi-Paxos)