一致性共识算法
什么是一致性共识算法?
在分布式系统中,即使出现网络故障、节点宕机、消息延迟或丢失的情况下,依然能够让众多节点就某个值达成一致意见的协议。
理论基础 Paxos 共识算法。 Raft 是易于理解的版本.
Paxos 算法(CP)
少数服从多数,最终达成一致。
基本概念
- Proposer: 提案人
- Proposal:提案
- proposal id
- value 决策
- Proposal:提案
- acceptor 决策者
- learner 学习者
约束
- 决策(value)只有被提案人(proposer)提出之后才能被批准(chosen)
- 一次 paxos 算法执行的实例中,只批准(chosen)一个值(value)
- 学习者(learners)只能获得被批准(chosen)的提案(value)
paxos 算法就是对上面的约束不断加强得到的。
情景一:一个分布式系统,存在多个提案人同时提出提案,多个决策者接收提案。
proposer1 --> 提出 value=A
proposer2 --> 提出 value=B
acceptor1
acceptor2
acceptor3
注意:这些提案中,只有一个提案会被接收
一个提案必须被半数以上的 acceptor 接收
P1 约束
一个 acceptor 必须接收第一次收到的提案
P2 约束
如果某个 value 为 v 的提案被批准,那么之后批准的提案必须具有 value v
P2a
一旦一个具有 value v 的提案被批准,那么之后任何 acceptor 再次接收的提案必须具有 value v
P2b
一旦一个具有 value v 的提案被批准,那么以后任何 proposer 提出的提案必须具有 value v
p2c
如果一个编号为 n 的提案具有 value v,该提案被提出,那么存在一个多数派,要么他们中所有人都没有接受(accept)编号小于 n 的任何提案(这里指的是 n 是 acceptor 接收到第一个提案),要么他们已经接受(accept)的所有编号小于 n 的提案中编号最大的那个提案具有 value v(这里指的是在接收编号 n 提案之前,已经接收到了其他编号小于 n 的提案)。
p1a
当且仅当 Acceptor 没有回应过编号大于 n 的 prepare 请求时,Acceptor 接受(accept)编号为 n 的提案。
paxos 算法的 2 个阶段
Prepare(N) :表示 proposer 向 acceptor 发起一个编号为 N 的准备(prepare)请求。只有编号,没有值
Promise(N,I,V)
- acceptor 向 proposer 承诺,不再接收(accept)编号小于 N 的请求
- 此时 acceptor 还是会接收任何 prepare 请求的。
- 向 proposer 返回已接收的最大提案(编号为 I,值为 V) I/V 可以为 null
Accept(N,V) 表示 proposer 向 acceptor 发起提案。
Accepted(N,V) 表示 acceptor 已接收编号为 N,值为 V 的提案了。
Reject(maxN) 表示 acceptor 已经承诺不在接收编号小于 maxN 的提案。
paxos 算法的核心:如果(在 prepare 阶段发现)某个值已经被选定,那么后续所有被选定的提案都必须是同一个值
准备阶段(发现已有共识)
-
proposer 在提出一个提案前,要与超过半数的 acceptor 进行通信,向它们发送编号为 N 的 Prepare 请求。 (Prepare(N))
-
acceptor 在接收到编号为 N 的 prepare 请求后,会进行判断
- 如果之前没有响应过任何 Prepare 请求,那么会接收编号为 N 的请求,同时向 proposer 承诺不会再接收编号小于 N 的 Prepare 请求 (Promise(N,null,null))
- 如果编号 N 小于已经响应过的 Prepare 请求的编号,则拒绝提案。 Reject(maxN)
- 如果编号 N 大于自身已经响应过的所有 Prepare 请求的编号,那么它会将它以前接收过的编号最大的提案作为响应反馈给 proposer,同时承诺不再接收任何编号小于 N 的提案 (Promise(N,M,Vm))
批准阶段(达成新共识)
-
对 prepare 阶段的响应结果进行分析:
- 如果没有超过半数的节点 promise,则增加编号,并等到一段时间之后,再进入 prepare 阶段。
- 有超过半数的节点 promise
- 如果发现有 acceptor 已经接受过其他编号的提案(编号为 M, 值为 Vm),那么 proposer 会将要值修改为 Vm
accept(N,Vm) - 如果没有发现 acceptor 获取到其他提案
accept(N,V)
- 如果发现有 acceptor 已经接受过其他编号的提案(编号为 M, 值为 Vm),那么 proposer 会将要值修改为 Vm
-
向过半数的 Acceptor 发送
accept(N,V)或accept(N,Vm)取决于 prepare 阶段是否有收到其他 acceptor 响应的其他编号的值。 -
acceptor 接收到请 accept 请求后会进行判断
-
如果没有响应过编号大于 N 的 prepare 请求---> 接收提案。
-
如果已经响应过编号大于 N 的 prepare 请求---> 拒绝
-
过半数 acceptor 同意,
accepted(N,V), proposer 会向 learner 广播 -
没有过半数的 acceptor 同意,增大提案编号,等待一段时间,重新进入准备阶段。
-
proposer acceptor
-----> prepare(N)
1. maxN == null ,value ==null{
maxN = n
<-----return promise(N)
}
2. maxN != null, value ==null
2.1 maxN > N
<-----return reject(maxN)
2.2 maxN <= N
maxN = n
<-----return promise(N)
3. maxN != null, value != null
3.1 maxN > N
<-----return reject(maxN)
3.2 maxN <= N
<-----return promise(N,maxN,value)
承诺不在响应编号 < N 的 prepare
不在接受编号 < N 的 accept
活锁问题
multi-paxos 算法
multi-paxos 针对多值序列。
Multi-paxos 首先会选取出一个 leader 节点。
选取 leader 的方式:通过一个 prepare 阶段,获取多数节点 promise 的节点成为 leader。
后续客户端所有的请求都由 leader 节点处理。
multi-paxos 性能相对于 paxos 提升了 1 倍(多值序列,只用一次 prepare 阶段)。
multi-paxos 避免了活锁问题。