Skip to content

Commit

Permalink
fix typo
Browse files Browse the repository at this point in the history
  • Loading branch information
isno committed Feb 2, 2025
1 parent ba3fc10 commit 8661938
Show file tree
Hide file tree
Showing 12 changed files with 51 additions and 58 deletions.
38 changes: 18 additions & 20 deletions consensus/Basic-Paxos.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
# 6.3.2 Paxos 算法
# 5.3.2 Paxos 算法

希望你没有对前篇 Paxos 的“复杂”做的铺垫所吓倒,共识问题已经算是一个古老的领域,30 余年间已经有无数简洁直白的视频、论文等资料进行过解读。网络中流传甚广的 Raft 和 Paxos 视频讲解[^1],即使没有多少技术背景,也能通俗地理解 Paxos。

接下来,我们先了解 Paxos 基本背景,然后直面 Paxos 算法细节,最后再用具体的例子验证 Paxos 算法。
希望你没有对前篇 Paxos 的“复杂”做的铺垫所吓倒,共识问题已经算是一个古老的领域,30 余年间已经有无数简洁直白的视频、论文等资料进行过解读[^1]。接下来,我们先了解 Paxos 基本背景,然后直面 Paxos 算法细节,最后再用具体的例子验证 Paxos 算法。

## 1. Paxos 算法背景

在 Paxos 算法中,节点分为下面三种角色:

- **提议者(Proposer)**:提议者是启动共识过程的节点,它提出一个值,请求其他节点对这个值进行投票,提出值的行为称为发起“提案"(Proposal),提案包含提案编号 (Proposal ID) 和提议的值 (Value)。注意的是,Paxos 算法是一个典型的为“操作转移”模型设计的算法,为简化表述,本文把提案类比成“变量赋值”操作,但你应该理解它是“操作日志”相似的概念,后面介绍的 Raft 算法中,直接就把“提案”称做“日志”了。
- **决策者(Acceptor)**:接受或拒绝提议者发起的提案,如果一个提案被超过半数的决策者接受,意味着提案被“批准”(accepted)。提案一旦被批准,意味着在所有节点中达到共识,便不可改变、永久生效。
- **记录者(Learner)**:记录者不发起提案,也不参与决策提案,它们学习、记录被批准的提案。
- **提议者(**Proposer):提议者是启动共识过程的节点,它提出一个值,请求其他节点对这个值进行投票,提出值的行为称为发起“提案"(Proposal),提案包含提案编号 (Proposal ID) 和提议的值 (Value)。注意的是,Paxos 算法是一个典型的为“操作转移”模型设计的算法,为简化表述,本文把提案类比成“变量赋值”操作,但你应该理解它是“操作日志”相似的概念,后面介绍的 Raft 算法中,直接就把“提案”称做“日志”了。
- **决策者**(Acceptor):接受或拒绝提议者发起的提案,如果一个提案被超过半数的决策者接受,意味着提案被“批准”(accepted)。提案一旦被批准,意味着在所有节点中达到共识,便不可改变、永久生效。
- **记录者**(Learner):记录者不发起提案,也不参与决策提案,它们学习、记录被批准的提案。

在 Paxos 算法中,节点都是平等的,它们都可以承担一种或者多种角色。例如,提议者即可发起提案,也可以就一个提案进行表决,但为了 Quorum 的计算更加明确,表决提案的节点数最好是奇数个。

Paxos 是基于 Quorum 的算法,但“少数服从多数”并不能解决所有的问题。例如,由于并发操作导致的提案冲突。

如图 6-5 所示,S~1~ 向 S~1~、S~2~、S~3~ 发起提案(red)。同时,S~5~ 也向 S~3~、S~4~、S~5~ 发起提案(blue)。它们的提案 Quorum 都达成了,也就是说一个提案有两个值被批准,这显然破坏了一致性原则。
如图 5-5 所示,S~1~ 向 S~1~、S~2~、S~3~ 发起提案(red)。同时,S~5~ 也向 S~3~、S~4~、S~5~ 发起提案(blue)。它们的提案 Quorum 都达成了,也就是说一个提案有两个值被批准,这显然破坏了一致性原则。

:::center
![](../assets/paxos_2pc_choice.png) <br/>
6-5 网络延迟导致冲突
5-5 网络延迟导致冲突
:::

根据图 6-5,你会发现提案冲突发生在 S~3~,S~3~ 是两个 Quorum 的交集点,它的时间线上有两个不同的值被批准。
根据图 5-5,你会发现提案冲突发生在 S~3~,S~3~ 是两个 Quorum 的交集点,它的时间线上有两个不同的值被批准。

我们知道,设计程序的一个基本常识是,如果多个线程同时操作某个共享变量,一定要加上互斥锁,不然会出现各种意外情况。不难发现,S~3~ 问题的本质是:“在分布式环境下并发操作共享变量的问题”。

因为分布式环境下随时可能出现通信故障,我们不能“套用”进程加锁的机制解决 S~3~ 的问题。举个例子,假如一个节点获得锁之后,释放锁之前故障了。那么,整个系统就会被无限期等待阻塞。

解决上述问题的关键在于,得有一种可供其他节点抢占锁的机制,避免因通信故障导致死锁。

笔者在 5.4.2 节 介绍过“乐观锁”分布式抢占锁的设计思想和“乐观锁”有异曲同工之妙。回顾乐观锁的示例 SQL,WHERE 条件的作用是判断在它操作之前,数据是否被修改。如果修改过,则请求最新的数据,更新版本号,然后用重试的方式再次修改。
笔者在本书第四章 4.2 节 介绍过“乐观锁”分布式抢占锁的设计思想和“乐观锁”有异曲同工之妙。回顾乐观锁的示例 SQL,WHERE 条件的作用是判断在它操作之前,数据是否被修改。如果修改过,则请求最新的数据,更新版本号,然后用重试的方式再次修改。

```SQL
UPDATE accounts
Expand All @@ -40,7 +38,7 @@ SET balance = balance + ?,
WHERE id = ? AND version = ?;
```

我们可以借鉴“乐观锁”的思路,尝试解决图 6-5 中的并发冲突问题。请看下面的操作:
我们可以借鉴“乐观锁”的思路,尝试解决图 5-5 所示的冲突问题。请看下面的操作:

首先,S~1~ 发起提案,S~3~ 收到 S~1~ 提案时,应该意识到 S~5~ 发起的提案(blue)的 Quorum 已经达成,S~1~ 提案(red)已经失效。根据先到先得原则,S~1~ 应该更新自己的提案值(red 替换为 blue),这个操作相当于对提案编号(乐观锁中的 version)“锁定”,防止之后出现多个冲突的提案编号。

Expand All @@ -62,7 +60,7 @@ Paxos 算法的第二个阶段称“批准阶段”(Accept)。提议者向

:::center
![](../assets/paxos.svg) <br/>
6-6 Paxos 算法流程
5-6 Paxos 算法流程
:::

## 3. Paxos 算法验证
Expand All @@ -73,36 +71,36 @@ Paxos 算法的第二个阶段称“批准阶段”(Accept)。提议者向

现在,我们来分析 S~1~ 、S~5~ 同时发起提案,会出现什么情况。

**情况一:提案已批准**。如图 6-7 所示,S~1~ 收到客户端的请求,于是 S~1~ 作为提议者,向 S~1~...S~3~ 广播 Prepare(3.1) 消息,决策者 S~1~...S~3~ 没有接受过任何提案,所以接受该提案。接着,S~1~ 广播 Accept(3.1, X) 消息,提案 X 成功被批准。
**情况一:提案已批准**。如图 5-7 所示,S~1~ 收到客户端的请求,于是 S~1~ 作为提议者,向 S~1~...S~3~ 广播 Prepare(3.1) 消息,决策者 S~1~...S~3~ 没有接受过任何提案,所以接受该提案。接着,S~1~ 广播 Accept(3.1, X) 消息,提案 X 成功被批准。

在提案 X 被批准后,S~5~ 收到客户端的提案 Y,S~5~ 作为提议者向 S~3~...S~5~ 广播 Prepare(4.5) 消息。对 S~3~ 来说,4.5 比 3.1 大,且已经接受了 X,它回复提案 (3.1, X)。S~5~ 收到 S~3~...S~5~ 的回复后,使用 X 替换自己的 Y,接着进入批准阶段,广播 Accept(4.5, X) 消息。S~3~...S~5~ 批准提案,所有决策者就 X 达成一致。

:::center
![](../assets/paxos-p1.png) <br/>
6-7 提案已批准
5-7 提案已批准
:::

**情况二:事实上,对于情况一,也就是“取值为 X”并不是一定需要多数派批准,S~5~ 发起提案时,准备阶段的应答中是否包含了批准过 X 的决策者也影响决策**。如图 6-8 所示,S~3~ 接受了提案 (3.1, X),但 S~1~、S~2~ 还没有收到 Accept(3.1, X) 消息。此时 S~3~、S~4~、S~5~ 收到 Prepare(4.5) 消息,S~3~ 回复已经接受的提案 (3.1, X),S~5~ 将提案值 Y 替换成 X,广播 Accept(4.5, X) 消息给 S~3~、S~4~、S~5~,对 S~3~ 来说,编号 4.5 大于 3.1,所以批准提案 X,最终共识的结果仍然是 X。
**情况二:事实上,对于情况一,也就是“取值为 X”并不是一定需要多数派批准,S~5~ 发起提案时,准备阶段的应答中是否包含了批准过 X 的决策者也影响决策**。如图 5-8 所示,S~3~ 接受了提案 (3.1, X),但 S~1~、S~2~ 还没有收到 Accept(3.1, X) 消息。此时 S~3~、S~4~、S~5~ 收到 Prepare(4.5) 消息,S~3~ 回复已经接受的提案 (3.1, X),S~5~ 将提案值 Y 替换成 X,广播 Accept(4.5, X) 消息给 S~3~、S~4~、S~5~,对 S~3~ 来说,编号 4.5 大于 3.1,所以批准提案 X,最终共识的结果仍然是 X。

:::center
![](../assets/paxos-p2.png) <br/>
6-8 提案部分接受,新提议者可见
5-8 提案部分接受,新提议者可见
:::

**情况三:另外一种可能的情况是 S~5~ 发起提案时,准备阶段的应答中未包含批准过 X 的决策节点**。S~1~ 接受了提案 (3.1, X),S~3~ 先收到 Prepare(4.5) 消息,后收到 Accept(3.1, X) 消息,由于 3.1 小于 4.5,会直接拒绝这个提案。提案 X 没有收到多数的回复,X 提案就被阻止了。提案 Y 顺利通过,整个系统最终对“取值为 Y”达成一致。

:::center
![](../assets/paxos-p3.png) <br/>
6-9 提案部分接受,新提议者不可见
5-9 提案部分接受,新提议者不可见
:::


**情况四:从情况三可以推导出另一种极端的情况**,多个提议者同时发起提案,在准备阶段互相抢占,反复刷新决策者上的提案编号,导致任何一方都无法达到多数派决议,这个过程理论上可以无限持续下去,形成“活锁”(livelock)。

解决这个问题并复杂,随机重试时间,就能减少这种巧合发生。
解决这个问题并不复杂,将重试时间随机化,就能减少这种巧合发生。
:::center
![](../assets/paxos-p4.png) <br/>
6-10 出现活锁问题
5-10 出现活锁问题
:::

以上,就是整个 Paxos 算法的工作原理。
Expand Down
4 changes: 2 additions & 2 deletions consensus/Paxos-history.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 6.3.1 Paxos 算法起源
# 5.3.1 Paxos 算法起源

Paxos 算法最初的论文名称为《The Part-Time Parliament》,翻译成中文为“兼职议会”。

Expand Down Expand Up @@ -34,7 +34,7 @@ Paxos 算法最初的论文名称为《The Part-Time Parliament》,翻译成

:::center
![](../assets/paxos.png) <br/>
6-4 《Paxos Made Simple》论文摘要
5-4 《Paxos Made Simple》论文摘要
:::

然而,这篇论文还是非常难以理解,引用斯坦福大学学者 Diego Ongaro 和 John Ousterhout 在设计 Raft 时的论文[^4]中对 Paxos 的描述:
Expand Down
2 changes: 1 addition & 1 deletion consensus/Paxos.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 6.3 Paxos 算法
# 5.3 Paxos 算法

Paxos 算法由 Leslie Lamport[^1] 于 1990 年提出,是一种基于消息传递、具备高度容错特性的共识算法。该算法是当今分布式系统最重要的理论基础,几乎就是“共识系统”的代名词。

Expand Down
12 changes: 6 additions & 6 deletions consensus/Replicated-State-Machine.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# 6.2 日志与复制状态机
# 5.2 日志与复制状态机

如果统计分布式系统有多少块基石,“日志”一定是其中之一。

这里“日志”并不是常见的通过 log4j 或 syslog 输出的文本。而是 MySQL 中的 binlog(Binary Log)、MongoDB 中的 Oplog(Operations Log)、Redis 中的 AOF(Append Only File)、PostgreSQL 中的 WAL(Write-Ahead Log)...。它们虽然名称不同,但共同特点是**只能追加、完全有序的记录序列**

根据图 6-1 展示了日志结构,可以看出日志是有序的、持久化的记录序列,增加记录时从末尾追加,读取时“从左到右”顺序扫描。
根据图 5-1 展示了日志结构,可以看出日志是有序的、持久化的记录序列,增加记录时从末尾追加,读取时“从左到右”顺序扫描。

:::center
![](../assets/log.png) <br/>
6-1 日志是有序的、持久化的记录序列 [图片来源](https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying)
5-1 日志是有序的、持久化的记录序列 [图片来源](https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying)
:::

有序的日志记录了“什么时间发生了什么”,这句话通过下面两种数据复制模型来理解:
Expand All @@ -18,12 +18,12 @@

:::center
![](../assets/active_and_passive_arch.png) <br/>
6-2 分布式系统数据复制模型
5-2 分布式系统数据复制模型
:::

无论哪一种模型,它们都揭示了:“**顺序是节点之间保持一致性的关键因素**”。如果打乱了操作的顺序,就会得到不同的运算结果。

进一步解释以“复制状态机”(State Machine Replication)工作模型构建的分布式系统,其原理如图 6-3 所示。
进一步解释以“复制状态机”(State Machine Replication)工作模型构建的分布式系统,其原理如图 5-3 所示。

:::tip 复制状态机的基本原理
两个“相同的” (identical)、“确定的” (deterministic) 进程:
Expand All @@ -45,7 +45,7 @@

:::center
![](../assets/Replicated-state-machine.webp) <br/>
6-3 复制状态机工作模型 [图片来源](https://raft.github.io/raft.pdf)
5-3 复制状态机工作模型 [图片来源](https://raft.github.io/raft.pdf)
:::

[^1]: https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying
6 changes: 3 additions & 3 deletions consensus/conclusion.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# 6.5 小结
# 5.5 小结

尽管 Paxos 算法提出已有几十年,但它为分布式共识研究奠定了基础。Paxos 基于“少数服从多数”(Quorum 机制)原则,通过“请求阶段”和“批准阶段”在不确定环境下,解决了单个“提案”的共识问题。多次运行 Paxos,便可实现一系列“提案”的共识,这就是 Multi-Paxos 的核心思想。Raft 算法在 Multi-Paxos 的基础上,在一致性、安全性和可理解性之间找到平衡,成为业界广泛采用的主流选择。

接下来,再思考一个问题,Raft 算法属于“强领导者”模型,领导者负责所有写入操作,它的写瓶颈就是 Raft 集群的写瓶颈。那么,该如何突破 Raft 集群的写瓶颈呢?

一种方法是使用哈希算法将数据划分成多个独立部分。例如,将一个 100TB 规模数据的系统分成 10 部分,每部分只需处理 10TB。这种根据规则(范围或哈希)将数据分散处理的策略,被称为“分片机制”(Sharding)。分片机制广泛应用于 Prometheus、Elasticsearch 、ClickHouse 等大数据系统(详见本书第九章)。理论上,只要机器数量足够,分片机制便能支持几乎无限规模的数据。
一种方法是使用哈希算法将数据划分成多个独立部分(分片)。例如,将一个 100TB 规模数据的系统分成 10 部分,每部分只需处理 10TB。这种根据规则(范围或哈希)将数据分散处理的策略,被称为“分片机制”(Sharding)。分片机制广泛应用于 Prometheus、Elasticsearch 、ClickHouse 等大数据系统(详见本书第九章)。理论上,只要机器数量足够,分片机制便能支持几乎无限规模的数据。

解决了数据规模的问题,接下来的课题是“将请求均匀地分摊至各个分片”,笔者将在下一章《负载均衡》展开讨论。
解决了数据规模的问题,接下来的课题是“将请求均匀地分摊至各个分片”。这部分内容,笔者将在下一章《负载均衡与代理技术》展开讨论。
2 changes: 1 addition & 1 deletion consensus/consensus.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 6.1 什么是共识
# 5.1 什么是共识

业内讨论 Paxos 或 Raft 算法时,常使用“分布式一致性协议”或“分布式一致性算法”来描述。例如,Google Chubby 系统的作者 Mike Burrows 曾评价 Paxos:“There is only one consensus protocol...”,这一句话常被翻译为“世界上只有一种一致性算法”。

Expand Down
Loading

0 comments on commit 8661938

Please sign in to comment.