diff --git a/assets/paxos.svg b/assets/paxos.svg
new file mode 100644
index 00000000..55fe2c8b
--- /dev/null
+++ b/assets/paxos.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/consensus/Basic-Paxos.md b/consensus/Basic-Paxos.md
index 2366d4d4..570814c4 100644
--- a/consensus/Basic-Paxos.md
+++ b/consensus/Basic-Paxos.md
@@ -2,57 +2,13 @@
希望你没有对前篇 Paxos 的“复杂”做的铺垫所吓倒,共识问题已经算是一个古老的领域,30 余年间已经有无数简洁直白的视频、论文等资料进行过解读。譬如在网络中流传甚广的 raft 和 paxos 视频讲解[^1],即使没有多少技术背景,也能通俗地理解 Paxos。
-这一节,我们从故事回到算法本身,正式开始学习 Paxos。Paxos 算法包含两个部分:
-- 其中一部分是**核心算法**(Basic Paxos);
-- 另外一部分是基于核心算法扩展的**完整算法**(Multi Paxos)。
-
-在笔者看来 Basic Paxos 是 Multi-Paxos 思想的核心,说直接点 Multi-Paxos 就是多执行几次 Basic Paxos,所以掌握了 Basic Paxos,我们便能更好的理解后面基于 Multi-Paxos 思想的共识算法(譬如 raft 算法)。
-
-那么接下来我们就看看 Basic Paxos 是如何解决共识问题的。
-
-:::tip 背景设定
-
-很久以前,在遥远的爱琴海上,有一座与世隔绝的小岛,叫做 Paxos。。。算了,还是换个更对口味的例子吧。
-
-从前有个村,老村长退休了,需要选一个新村长,选取新村长的事件称之为提案(Proposal)。张三、李四都想当村长(张三、李四我们称为提议节点,Proposer),但当村长需要多位村委(决策节点,Acceptor)的投票同意,村委使用少数服从多数投票机制。选举结束之后,要把结果同步给村民(记录节点,Learner)。
-:::
-
-如果继续把问题讲下去,笔者似乎通篇都要讲张三、李四的段子,我们还是把背景再转换为分布式工程问题吧。
-
-假如我们设计一个由三个节点 A、B、C(3 个村委)组成分布式集群,提供只读的 KV 存储服务。既然要创建一个只读服务,必须先要对只读变量赋值,而且后续不能对该变量进行修改(村长选定了,结果就不能再更改)。所以,多个节点中,所有的节点必须要先对只读变量的值(提案)达成共识,然后所有的节点再一起创建这个只读变量。
-
-如图 6-3 所示,当有多个客户端(张三、李四,Proposer)访问这个系统,试图创建同一个只读变量(发起一个提案 Proposal,set x=1,提议张三当村长)时,集群中所有的节点(村委)该如何达成共识,实现各个节点中的 x 值的一致呢(所有的民村一致认为张三是村长)?
-
-:::center
- ![](../assets/paxos-consensus.svg)
- 图 6-3 如何实现多个节点 x 值一致性
-:::
实现多个节点 x 值一致的复杂度主要来源于以下两个因素的共同影响:
-1. 系统内部各个节点的**通信是不可靠的**,不论是系统中企图设置数据的提议节点,还是决定操作是否批准的决策节点,其发出、收到的信息都可能存在延迟、丢失的情况。
-2. 客户端**写入是并发的**,如果是串行的修改数据,仅单纯使用少数服从多数原则,就足以保证数据被正确读写。而并发访问就变成了“**分布式环境下多个节点并发操作共享数据**”的问题。
-
-我们把上面的背景问题总结转化,其实就是下面 2 个核心需求:
-
-1. **安全性** Safety:
- - 一个变量只会被确定一个值(只能一个人当村长);
- - 一个变量只有值被确定之后,才能被学习。
-2. **活性** Liveness:
- - 提案最终会被接受(一定能选出个村长);
- - 一个提案被接受之后,最终会被所有的村民(Learner)学习到;
- - 必须在有限时间内做出决议(不能有太多轮投票)。
-
-Basic Paxos 问题背景相信已经讲清楚了,那怎么解决?
-
-简单的方案如同图 6-4 所示,多个提议节点、单个决策节点,决策节点接受第一个发给它的值,作为被选中的值。但如果决策节点故障,整个系统就会不可用。
-
-:::center
- ![](../assets/quorum-base.png)
- 图 6-4 只有一个决策节点会有单点故障
-:::
+- 分布式系统是不可靠的:节点可能宕机,节点间通信可能延迟、丢失、乱序;
+- 操作是并发的:如果是串行操作,单纯使用少数服从多数原则,就足以保证正确读写数据。但并发操作就变成了“**分布式环境下多个节点并发操作共享数据**”的问题。
-为了克服单点故障问题,借鉴多数派的机制,思路是写入一半以上的节点,如果集群中有 N 个节点,客户端需要写入 W >= N/2 + 1 个节点。使用多数派的机制后最多可容忍 (N-1)/2 个节点故障。
+第一问题相对好解决。一个节点不可靠无所谓,只要一群节点中多个节点可靠就行。按照少数服从多数原则,也就是 Quorum 机制。
:::tip Quorum
@@ -113,34 +69,33 @@ Once a value has been chosen, future proposals must propose the same value.
## 1. Basic Paxos 算法描述
-Basic Paxos 对上述问题的解决方案是定义一个 Proposal Number 标识唯一的提案。
-一个简单的 Proposal Number 的定义为:``,seq_id 可以是一个自增的 ID,同时为了避免崩溃重启,必须能在本地持久化存储,最后再拼接上 server_id,确保是分布式系统中唯一 ID。
+“准备阶段”(Prepare)相当于抢占锁的过程。某个提案节点准备发起提案,必须先向所有的决策节点广播一个许可申请(称为 Prepare 请求)。Prepare 请求附带一个全局唯一且单调递增的数字 n 作为提案 ID。决策节点收到请求后,将给予提案节点两个承诺与一个应答:
+- [承诺1]:不再接受提案 ID <= n 的 Prepare 请求;
+- [承诺2]:不再接受提案 ID < n 的 Accept 请求;
+- [应答]:不违背历史承诺的前提下,回复形成决议最大的那个提案所设定的值和提案 ID:
+ - 如果这个值没有被任何提案设定过,则返回空值
+ - 如果违反历史承诺,即收到的提案 ID 并不是决策节点收到的最大的 ID,对 Prepare 请求不予理会。
-当决策节点收到这个提案后,将会给两个承诺一个应答。
+提案节点收到多数决策节点的应答(称为 Promise 应答)后,就可以开始“批准”(Accept)阶段了。
-- **两个承诺**:
- - 承诺不会再接受提案 ID 小于或者等于 n 的 Prepare 请求;
- - 承诺不会再接受提案小于 n 的 Accept 请求。
-- **一个应答**:
- - 不违背以前作出的承诺下,回复已经 Accept 过的提案中提案 ID 最大的那个提案的值和提案 ID,没有则返回空值。
+提议者向所有接受者广播 accept(n, v) 请求,请求接受提案编号为 n 和提案值为 v。
+- 如果提案编号 n >= 该接受者之前所承诺的编号,它将接受该提案,并返回 accepted(n, v) 消息。
+- 如果提案编号 n < 该接受者所承诺的编号,它将拒绝该提案。
-再具体一点描述 Basic Paxos 算法:
-- 首先是**准备阶段**,选择一个提交号 n,提交 Prepare(n),接受者需要返回自己接受的值和已经接受的提交号。当从大多数收到回复以后就可以做判断了,如果有返回接受值,选择提交号最大的值进行下一阶段(这个行为对应的是发现有值可能被接受了,尝试服从或者学习这个接受),不然就可以用自己的值进行下一阶段。
-- 下一阶段就是**接受阶段** accept(value,n),如果接受者发现自己目前收到的 n,没有比 accpet 给的 n 大,就接受这个值,并且更新自己的 n,否则就拒绝(这里就保证提交者能够发现自己变老了或者被拒绝了)。如果接受者发现提交号大于自己当前的最大提交号,就接受这个值,不然就拒绝。当提交者从大多数人那里接受到返回以后发现有拒绝的情况,就进行重试拿一个新的 n 开始,否则这个值就被接受了。
+如果提案节点收到了大多数决策节点的应答(accepted),协商结束,共识协议形成,然后将形成的决议发送给所有记录节点进行学习。
-总结 Basic Paxos 中的值就是设置一次,不存在再设置一次的情况,整个流程如下图 6-8 所示。
:::center
- ![](../assets/basic-paxos.png)
+ ![](../assets/paxos.svg)
图 6-8 Basic Paxos 流程
:::
## 2. Basic Paxos 验证
-通常说 Paxos 算法是复杂算法难以理解是指其推导过程复杂。理论证明一个 Paxos 的实现,比实现这个 Paxos 还要难。我们假设下面几种情况看看能不能解决前面的问题。
+那这样的二阶段提交算法是否可以解决前面提到的问题?我们通过具体的例子来分析。
-假设一个分布式系统中有五个节点,分别是 S~1~、S~2~、S~3~、S~4~、S~5~,这 5 个节点同时扮演着提案节点和决策节点的角色。此时,有两个并发请求希望将同一个值分别设定为 X(由 S~3~ 作为提案节点提出)和 Y(由 S~5~ 作为提案节点提出),以 P 代表准备阶段,以 A 代表批准阶段,这时会发生以下几种情况。
+假设共有五个节点 S~1~、S~2~、S~3~、S~4~、S~5~,这 5 个节点同时扮演着提案节点和决策节点的角色。此时,有两个并发请求希望将同一个值分别设定为 X(由 S~3~ 作为提案节点提出)和 Y(由 S~5~ 作为提案节点提出),图中的 P 代表准备阶段,A 代表批准阶段,这时会发生以下几种情况。
**情况一:提案已 Chosen** 譬如,S~1~ 选定的提案 ID 是 3.1(全局唯一 ID 加上节点编号),先取得了多数派决策节点的 Promise 和 Accepted 应答,此时 S~5~ 选定提案 ID 是 4.5,发起 Prepare 请求,收到的多数派应答中至少会包含 1 个此前应答过 S~1~ 的决策节点,假设是 S~3~,那么 S~3~ 提供的 Promise 中必将包含 S~1~ 已设定好的值 X,S~5~ 就必须无条件地用 X 代替 Y 作为自己提案的值,由此整个系统对“取值为 X”这个事实达成一致。整个流程如下图所示。