01 分布式系统的本质难题

摘要:

在深入学习 CAP 定理、Paxos、Raft 这些具体的理论和协议之前,有必要先搞清楚一个更本质的问题:分布式系统为什么难? 不是”有一些工程上的麻烦”那种难,而是有深刻的数学和物理根源的难——某些问题在分布式环境中理论上就比在单机上更复杂,这种复杂性无法通过更高明的编程技巧消除,只能在特定假设下被”规避”。本文从三个维度解构这种本质难题:网络的不可靠性(消息可能丢失、延迟、乱序、重复)、时钟的不一致性(物理时钟漂移、逻辑时钟的局限)、以及节点的局部视角(每个节点只能观察到自己看到的世界,无法直接知道其他节点的真实状态)。理解这三个难题,是理解一切分布式协议设计的出发点。


第 1 章 从单机到分布式:一次世界观的转变

1.1 单机编程的隐含假设

在写单机程序时,我们有很多从不需要质疑的”常识”:

假设一:操作要么成功,要么失败,没有”不知道”

调用一个函数,要么返回结果,要么抛出异常。即使发生了段错误(Segmentation Fault),操作系统也会告诉你进程崩溃了。你永远可以知道操作的结果是什么。

假设二:时间是单调递增的

System.currentTimeMillis() 返回的值在同一个 JVM 中是单调递增的(忽略 NTP 调整的极端情况)。先发生的事件,时间戳一定比后发生的事件小。

假设三:共享内存是一致的

多个线程访问同一块内存,只要用了正确的同步机制(synchronized/volatile),就能保证所有线程看到的数据是一致的。内存中的一个 int 变量,就是那一个值——不会有线程 A 看到的是 5,线程 B 同时看到的是 10。

假设四:局部失败会影响全局

如果一个关键模块崩溃,整个程序通常也随之崩溃(或进入错误状态),不会出现程序的某个部分正常运行、另一个部分崩溃、但两部分之间看不到对方的情况。

这四个假设在分布式系统中全部不成立。正是这四个假设的失效,构成了分布式系统难题的根源。

1.2 分布式系统:假设失效的世界

当程序从单机扩展到多台机器,上述四个假设依次崩溃:

假设一失效:操作结果可能是”未知”

客户端向服务器发送了一个请求,网络超时了——请求究竟有没有到达服务器?服务器执行了吗?服务器执行完后响应在网络中丢失了?还是根本就没到达?客户端无从得知。这不是可以通过重试解决的问题——重试可能导致操作被执行两次。

假设二失效:不同机器上的时间无法精确比较

机器 A 上的 2024-01-01 10:00:00.500 和机器 B 上的 2024-01-01 10:00:00.500 是否是同一时刻?不一定——两台机器的时钟可能有毫秒甚至秒级的偏差,即使经过 NTP 同步,也无法消除所有偏差。

假设三失效:没有共享内存

分布式系统中,机器 A 上的变量和机器 B 上的变量是两份独立的数据。A 修改了自己的副本,B 并不会自动感知到,需要通过网络通信来同步——而网络是不可靠的。

假设四失效:局部失败成为常态

在一个有 1000 台服务器的集群中,同时有 1 台服务器出问题是完全正常的(1000 台 × 每台年故障率 1% ≈ 10 台/年 ≈ 每月约 1 台故障)。局部失败不会波及其他节点——其他 999 台服务器还在正常运行,但它们看不到那台故障节点的状态。

分布式系统的定义

分布式系统(Distributed System)是由多台计算机组成,通过网络传递消息进行协作的系统。Leslie Lamport 给出了一个著名的(半开玩笑的)定义:“A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.” (一个分布式系统,是指一台你甚至不知道存在的机器的故障,能导致你自己的机器无法使用的系统。)

这个定义精准地抓住了分布式系统的核心特征:组件之间存在隐式依赖,局部失败会以意想不到的方式影响系统整体


第 2 章 网络不可靠:分布式系统的第一公敌

2.1 网络故障的分类学

在分析分布式协议之前,需要对网络故障有精确的分类认知。网络故障不是一个笼统的概念,不同类型的故障对协议设计的影响截然不同。

消息丢失(Message Loss)

消息在传输过程中完全消失,发送方和接收方都不知道。导致消息丢失的原因:路由器缓冲区溢出(网络拥塞)、网卡故障、物理链路中断。

消息延迟(Message Delay)

消息到达了,但比预期晚了很多。在互联网上,消息延迟可以从几毫秒(同机房)到几百毫秒(跨大洲),甚至因为 TCP 重传、拥塞控制等机制,延迟可能突增到秒级。

消息乱序(Message Reordering)

消息 A 在消息 B 之前发送,但 B 比 A 先到达。这在 UDP 协议中很常见(无顺序保证),在 TCP 中通常不发生(TCP 保证有序),但在应用层(多个并发 TCP 连接、消息队列等)乱序问题依然普遍。

消息重复(Message Duplication)

同一条消息被接收方收到了多次。这在网络层很少发生(IP 层有 TTL 机制),但在应用层通过重试实现可靠传输时极为常见:发送方因为超时认为消息丢失而重发,但实际上第一条消息只是延迟了,最终都到达了接收方。

消息篡改(Message Corruption)

消息内容在传输过程中被修改,通常由硬件故障(内存错误、网卡故障)引起。在有校验和(Checksum)的协议(如 TCP)中,篡改会被检测到并丢弃,退化为消息丢失问题。

2.2 同步网络 vs 异步网络:协议假设的分水岭

网络模型的选择是分布式协议设计最重要的假设之一,直接影响协议的可行性和性能:

同步网络模型(Synchronous Network Model)

定义:存在一个已知的消息传递时间上界 Δ,所有消息在 Δ 时间内必然到达(或被判定为丢失)。

在同步网络中,协议可以利用超时来精确判断故障:等待 Δ 时间后没有收到响应,就可以确定地判断对方已经崩溃或消息丢失。

异步网络模型(Asynchronous Network Model)

定义:消息传递没有时间上界,消息可能在任意长时间后到达。

在异步网络中,协议无法仅靠超时来区分”节点崩溃”和”消息延迟”——等待 10 秒没有回应,可能是节点崩溃了,也可能是消息还在路上(将在 11 秒后到达)。这是 FLP 不可能定理成立的关键前提(将在第 03 篇详述)。

现实网络是什么模型?

真实的网络既不是纯同步的,也不是纯异步的——它是**部分同步(Partially Synchronous)**的:大多数时候消息延迟有界(如 < 200ms),但偶尔会出现超出界限的延迟(如 GC 停顿、网络拥塞)。部分同步模型是 Paxos、Raft 等实用共识协议的基础假设——这些协议在正常情况下(网络同步)能够快速达成共识,在异常情况下(网络不同步)能够保证安全性,只是活性(Progress)可能暂时失去。

2.3 两将军问题:网络不可靠性的极限证明

两将军问题(Two Generals Problem) 是 1975 年由 Jim Gray 提出的一个经典思想实验,它证明了在不可靠的网络上,无法实现完美的协调:

场景描述:两支军队(将军 A 和将军 B)计划联合进攻一座城市。只有两支军队同时进攻才能获胜;单独进攻会失败。两将军之间只能通过信使传递消息,但信使穿越敌方区域时可能被俘(消息丢失)。

目标:将军 A 和将军 B 需要就”何时同时进攻”达成共识。

问题的本质

A 发送:"我们明天早上 8 点进攻。"
↓ 信使可能被俘
B 收到了消息,回复:"好的,明天早上 8 点。"
↓ 信使可能被俘
A 没有收到确认,不确定 B 是否知道进攻计划

A 再发送:"我已收到你的确认,我们明天进攻。"
↓ 信使可能被俘
B 不确定 A 是否收到了自己的确认

……无论交换多少条消息,总有最后一条消息的发送方不确定接收方是否收到了

数学证明(非形式化):假设经过 k 轮消息交换后,双方可以确定地达成共识。那么,考虑最后一条消息(第 k 条)的发送方——如果这条消息丢失,接收方不会改变行为(没有收到消息),因此发送方的行为也不应该依赖这条消息是否到达。但这意味着第 k 条消息是多余的,可以去掉,退化为 k-1 轮交换。归纳地,任意有限轮的消息交换都不够。

工程含义:两将军问题证明了在不可靠的网络上,无法实现完美的一次性原子提交(没有任何不确定性的强同步)。这正是 2PC(两阶段提交)存在阻塞问题的根本原因——它试图做一件理论上不完美的事情,只能接受在某些极端情况下(协调者崩溃)系统进入不确定状态。


第 3 章 时钟不一致:分布式系统的时间幻觉

3.1 为什么物理时钟无法信任

人类对时间的直觉是:时间是线性流逝的,所有人感知到的时间是同一个时间。在分布式系统中,这个直觉是危险的幻觉。

石英晶体的物理局限

现代计算机的时钟基于石英晶体振荡器。石英晶体在电流驱动下以固定频率振荡,计算机通过计数振荡次数来跟踪时间。问题是:每块石英晶体的振荡频率都略有不同,会随温度、压力、老化而变化。典型的石英晶体钟每天会积累约 10~100ms 的误差。

对于一个运行 24 小时的系统,两台机器的时钟可能相差 10ms 到 100ms。这听起来微不足道,但对于分布式系统来说可能是灾难性的:如果以时间戳决定操作顺序(如”谁的时间戳大,谁的写入获胜”),10ms 的误差就可能导致明明先发生的写入被后发生的写入”覆盖”。

NTP 的精度上限

网络时间协议(NTP,Network Time Protocol)通过从时间服务器同步时间来纠正本地时钟的漂移。然而:

  • NTP 的精度取决于网络延迟的对称性——如果从客户端到服务器的延迟与从服务器到客户端的延迟不对称,NTP 计算会引入误差
  • 在互联网上,NTP 的典型精度是 1~50ms
  • 在局域网内,精度可以达到 1ms 以内
  • 即使是 1ms 的误差,在高频交易等场景下也是不可接受的

时钟跳跃的危险

NTP 同步不仅可能带来误差,还可能引发时钟跳跃(Clock Jump):当本地时钟比 NTP 服务器慢了 5 秒时,NTP 可能将本地时钟直接向前调 5 秒,导致在 5 秒的时间窗口内,本地时钟从某个值突然跳到另一个值。

这对于依赖时间单调性的系统(如 TTL 计算、锁超时)是致命的:

Redis 分布式锁的 TTL 计算(如第02篇所述):
T0: 加锁,设置 TTL = 30 秒,本地记录 start_time = 1000
T10: 执行业务逻辑,经历 10 秒
T10: NTP 同步,本地时钟向前跳 25 秒(从 T10 变成 T35)
T35: 看门狗检查:now - start_time = 35 - 1000/1000 ... 计算结果异常
     或者:Redis TTL 在 T30 就已过期(物理 30 秒后),但应用认为才过了 10 秒
→ 锁已过期,但持锁方还在执行临界区操作

这正是 Kleppmann 在批评 Redlock 时提到的时钟跳跃问题的具体场景(详见第 03 篇分布式锁专栏)。

3.2 逻辑时钟:放弃物理时间,追求因果顺序

面对物理时钟的不可靠性,计算机科学家采取了一个务实的立场:放弃追求全局精确时间,改为追踪事件之间的因果关系

这个思想转变是由 Leslie Lamport 在 1978 年的论文 Time, Clocks, and the Ordering of Events in a Distributed System 中提出的,它引入了**逻辑时钟(Logical Clock)**的概念。

Lamport 时钟的核心思想

Lamport 时钟的核心观察是:我们真正关心的不是事件发生的物理时刻,而是事件之间的**“happens-before”(先发生于)**关系:

  • 如果事件 A 和事件 B 在同一个进程中,且 A 发生在 B 之前,则 A → B(A happened-before B)
  • 如果事件 A 是进程 P 发送消息 m,事件 B 是进程 Q 接收消息 m,则 A → B(因果关系)
  • 如果 A → BB → C,则 A → C(传递性)

Lamport 时钟算法

# 每个进程维护一个本地逻辑时钟 counter
counter = 0
 
# 规则 1:每次发生本地事件,counter 递增
def local_event():
    global counter
    counter += 1
    return counter
 
# 规则 2:发送消息时,将当前 counter 附在消息上
def send_message(msg, dest):
    global counter
    counter += 1
    send(dest, (msg, counter))  # 消息附带时间戳
 
# 规则 3:接收消息时,counter = max(本地 counter, 消息 counter) + 1
def receive_message():
    global counter
    msg, msg_timestamp = recv()
    counter = max(counter, msg_timestamp) + 1
    return msg

Lamport 时钟的保证:如果 A → B(A 在因果上先于 B),则 timestamp(A) < timestamp(B)

Lamport 时钟的局限:反过来不成立——timestamp(A) < timestamp(B) 不能推导出 A → B。两个没有因果关系的并发事件(进程 P 的事件 A 和进程 Q 的事件 B,两者之间没有消息传递),Lamport 时钟无法区分它们的顺序——只能给出一个任意的全序(通过进程 ID 打破平局),但这个全序不反映真实的因果关系。

向量时钟:Lamport 时钟的升级版

向量时钟(Vector Clock)解决了 Lamport 时钟的这个局限,能够精确追踪并发事件。向量时钟将在第 07 篇详细讲解,这里仅作概念引入:向量时钟用一个向量(每个进程一个计数器)代替单一计数器,能够严格地区分”有因果关系”和”并发”两种情况,是分布式存储(如 DynamoDBRiak)实现版本冲突检测的核心技术。

3.3 时间戳作为唯一 ID 的风险

在分布式系统的实践中,有一个常见的工程错误:用当前时间戳作为唯一 ID 或版本号

这个做法在单机上是安全的(时间单调递增,毫秒级粒度已经足够区分请求),但在分布式系统中存在严重隐患:

// 危险的做法:用时间戳作为分布式 ID
public String generateId() {
    return System.currentTimeMillis() + "-" + Thread.currentThread().getId();
}

问题一:同一毫秒内多个节点生成相同 ID

如果两台服务器在同一毫秒内各生成一个 ID,且它们的时钟恰好对齐,两个 ID 将完全相同(冲突)。

问题二:时钟回拨导致 ID 倒退

如果 NTP 因为本地时钟过快而将其调慢(时钟回拨),新生成的时间戳可能比之前生成的时间戳小,破坏了 ID 的单调性,导致数据库索引混乱、锁 TTL 计算错误等问题。

这正是 Snowflake 算法 诞生的原因——Twitter 设计的分布式 ID 生成方案,通过结合时间戳(高 41 位)+ 机器 ID(中 10 位)+ 序列号(低 12 位),在保证唯一性的同时,通过时钟回拨检测来保障 ID 单调性。


第 4 章 节点的局部视角:无法确知全局状态

4.1 每个节点都活在自己的”世界”里

分布式系统中最深层的挑战,是每个节点只能直接观察到自己的状态,通过网络消息来推断其他节点的状态——而网络消息是不可靠的、有延迟的。

这意味着:在任意时刻,任何一个节点对整个系统的全局状态的认知,都是不完整的、可能过时的快照

一个具体的例子:

集群中有三个节点:A、B、C

T0: A 是 Leader,B 和 C 是 Follower
T1: A 与 B、C 之间的网络链路发生故障(网络分区)
    → A 看到的世界:自己是 Leader,B 和 C 没有响应(可能故障?)
    → B 看到的世界:A 没有响应,C 正常,A 可能已经崩溃
    → C 看到的世界:A 没有响应,B 正常,A 可能已经崩溃

T2: B 和 C 发起选举,C 当选为新 Leader
    → B、C 的世界:C 是新 Leader
    → A 的世界:自己还是 Leader(不知道已经被替换)

T3: A 恢复与 B、C 的连接
    → 此时系统中"曾经存在两个 Leader"
    → 如果两个 Leader 都接受了写入,可能存在数据冲突

这个场景中,没有一个节点在 T1~T3 期间能够知道全局真实状态——A 不知道自己被替换,B 和 C 不知道 A 是否还在继续接受写入。这就是为什么 Raft、ZAB 等协议需要精心设计 Leader 任期(Term)和日志一致性协议,来处理这种”双主”(Split Brain)场景。

4.2 脑裂问题:局部视角的最危险后果

脑裂(Split Brain) 是指分布式系统因网络分区而导致多个节点各自认为自己是”唯一的权威”,并独立地对外提供服务(或接受写入),产生数据冲突。

脑裂是局部视角问题最严重的体现。以数据库主从复制为例:

正常状态:
  Master(节点 A)←──写入──  客户端
  Slave(节点 B) ←──复制── Master

网络分区后(A 和 B 之间的网络断开):
  A 的视角:自己是 Master,继续接受写入
            (A 认为 B 只是暂时网络不通,但自己仍然是主)
  B 的视角:看不到 A 的心跳,触发高可用切换,B 提升为新 Master

现在:
  A 也在接受写入(以为自己是 Master)
  B 也在接受写入(以为自己是新 Master)
  ↓
  两份数据产生分歧
  ↓
  网络恢复后,两份数据无法自动合并(有冲突)

脑裂问题的解决方案通常是多数派(Quorum)机制:规定只有获得超过半数节点认可的操作才能生效。如果集群有 5 个节点,即使网络分区成 2 个和 3 个,只有拥有 3 个节点的那一侧(多数派)才能对外服务,拥有 2 个节点的那一侧(少数派)会自动停止服务。两个分区不可能同时满足多数派条件(2 + 3 = 5,不可能两侧都 ≥ 3),因此不会出现两个 “Master” 同时工作的情况。

这正是 Paxos、Raft、ZAB 等共识协议的核心设计思想之一,将在后续篇章中深入展开。

4.3 不可靠的故障检测

每个节点需要判断其他节点是否还活着,以便在节点故障时触发相应的恢复机制(如重新选主)。但由于局部视角的限制,故障检测在分布式系统中本质上是不可靠的

节点 B 向节点 A 发送了心跳请求,A 没有在超时时间内响应。B 能从这个现象推断什么?

  • 可能性 1:A 真的崩溃了
  • 可能性 2:A 运行正常,但网络链路故障,A 的响应无法到达 B
  • 可能性 3:A 运行正常,但正在经历长时间的 GC 停顿,暂时无法响应
  • 可能性 4:A 运行正常,B 发出的心跳请求在网络中丢失了

在异步网络中,B 无法区分这四种情况,只能做出一个猜测(通常是”A 可能故障了,我需要触发故障处理流程”)。这个猜测可能是错误的,可能导致不必要的 Leader 切换。

完美故障检测器(Perfect Failure Detector)

在分布式系统理论中,完美故障检测器(Perfect Failure Detector) 是一个理论上的假设:它能够在有限时间内准确地检测到所有已崩溃的节点,且不会将正常节点误报为崩溃。

完美故障检测器的存在等价于同步网络模型——只有在已知消息传递时间上界的网络中,才能通过超时精确判断故障。在异步网络中,完美故障检测器不可能实现(这是 FLP 不可能定理证明中的关键引理,将在第 03 篇详述)。

实际系统(Raft、ZooKeeper)使用的是最终精确故障检测器(Eventually Perfect Failure Detector):允许短暂的误报(将正常节点误判为崩溃),但最终会纠正错误(一旦网络稳定后,检测结果变为精确的)。


第 5 章 拜占庭将军问题:最坏情况下的协调

5.1 故障模型的三个层次

在分布式系统理论中,对节点故障的建模有三个层次,从弱到强:

崩溃故障(Crash Failure):节点停止响应,不再发送任何消息。这是最简单的故障模型,Paxos、Raft、ZAB 等协议都假设节点只会崩溃,不会发送错误信息。

遗漏故障(Omission Failure):节点不发送或不接收某些消息(消息选择性丢失),但发送的消息是正确的。这比崩溃故障更难处理,但仍然是”诚实”的故障。

拜占庭故障(Byzantine Failure):节点可能发送任意错误的消息,包括发送矛盾的消息给不同的节点(欺骗),或与其他故障节点合谋。这是最强的故障模型。

在大多数工业场景中(数据中心内的服务器集群),假设节点只会崩溃(崩溃故障模型)是合理的:服务器故障通常是硬件损坏或软件崩溃,不会”撒谎”或”篡改数据”。因此,大多数生产级共识协议(Paxos、Raft、ZAB)假设崩溃故障模型。

5.2 拜占庭将军问题

拜占庭将军问题(Byzantine Generals Problem) 是由 Lamport、Shostak 和 Pease 在 1982 年提出的,专门分析在有恶意节点(拜占庭故障)的情况下如何达成共识:

场景:拜占庭帝国的 n 位将军包围了一座城市,计划协同进攻(或协同撤退)。将军之间通过信使通信。其中有最多 f 位将军是叛徒(拜占庭节点),他们会发送虚假的、矛盾的信息来阻止忠诚将军达成共识。

核心结论:要在有 f 个拜占庭节点的系统中达成共识,需要总节点数 n ≥ 3f + 1

为什么是 3f + 1?

考虑最简单的情况:f = 1(1 个叛徒),n 应该是多少?

  • 假设 n = 3(1 个叛徒,2 个忠诚将军):

    • 叛徒可以向将军 A 说”进攻”,向将军 B 说”撤退”
    • A 收到叛徒的”进攻”和 B 的消息,无法判断谁是叛徒
    • B 收到叛徒的”撤退”和 A 的消息,无法判断谁是叛徒
    • n = 3 时,1 个叛徒足以阻止达成共识
  • 假设 n = 4(1 个叛徒,3 个忠诚将军):

    • 每个将军收集其他所有将军的投票
    • 忠诚将军有 3 个,构成多数(3 > 4/2 = 2)
    • 叛徒无法让忠诚将军对事实产生分歧(多数派”压倒”叛徒)

拜占庭容错(BFT)协议的工程意义

区块链场景中,节点来自不可信的互联网参与方,必须假设存在恶意节点,因此使用拜占庭容错协议(PBFTHotStuff 等)。

数据中心场景中,节点是受信任的服务器,崩溃故障模型够用,使用 Paxos/Raft 即可,无需承担 BFT 协议的额外通信开销(BFT 协议的消息复杂度通常为 O(n²),比 Paxos/Raft 的 O(n) 高得多)。


第 6 章 分布式系统的工程现实:接受不确定性

6.1 不确定性是分布式系统的固有特性

经历了前五章的分析,一个结论是清晰的:分布式系统中存在根本性的不确定性,这种不确定性不是工程实现不够好造成的,而是网络的物理特性和异步网络的计算模型所决定的。

具体来说:

  • 节点无法在有限时间内确认其他节点是否还活着(故障检测不可靠)
  • 节点无法确认发出的消息是否被接收方处理了(两将军问题)
  • 节点无法依赖物理时钟来推断全局事件顺序(时钟不一致)

面对这些不可消除的不确定性,工程实践的出路是在特定假设下将不确定性降低到可接受的程度,而不是试图彻底消除它。

6.2 用于驯服不确定性的工程手段

手段一:超时(Timeout)

在异步网络中,超时是判断故障的唯一实用手段,尽管它不精确。关键是选择合适的超时阈值:过短会导致频繁的误判(将正常的延迟节点误认为故障),过长会导致故障恢复太慢。生产系统通常需要根据 P99 延迟来设置超时,留出足够的余量。

手段二:幂等性(Idempotency)

由于消息可能被重复发送(发送方超时后重试),接收方需要能够安全地处理重复消息——多次处理同一消息和处理一次的效果相同。这是处理”消息重复”问题的标准答案。

手段三:多数派(Quorum)

通过要求操作在多数节点上成功才算完成,确保即使有少数节点故障或网络分区,系统仍能正常工作,且任意两个多数派之间必然有交集(保证信息传播的一致性)。这是 Paxos、Raft 等协议的核心机制。

手段四:状态机复制(State Machine Replication)

如果所有副本从相同的初始状态出发,以相同的顺序执行相同的操作,最终状态必然相同。共识协议的核心工作就是保证所有副本看到相同的操作序列——这是构建强一致分布式系统的基石。

手段五:回退(Fallback)

在无法确定全局状态时,选择最保守的行为(如拒绝服务、返回旧数据),而不是做出可能错误的决策。这是 CP 系统(如 ZooKeeper)在网络分区时的典型选择。


参考资料

  1. Lamport, L. (1978). Time, Clocks, and the Ordering of Events in a Distributed System. Communications of the ACM, 21(7), 558–565.
  2. Lamport, L., Shostak, R., & Pease, M. (1982). The Byzantine Generals Problem. ACM Transactions on Programming Languages and Systems, 4(3), 382–401.
  3. Fischer, M.J., Lynch, N.A., & Paterson, M.S. (1985). Impossibility of Distributed Consensus with One Faulty Process. Journal of the ACM, 32(2), 374–382.
  4. Gray, J. (1978). Notes on Data Base Operating Systems. Operating Systems: An Advanced Course, Lecture Notes in Computer Science, vol. 60.
  5. Kleppmann, M. (2017). Designing Data-Intensive Applications. O’Reilly Media. Chapter 8: The Trouble with Distributed Systems.
  6. Tanenbaum, A., & Van Steen, M. (2016). Distributed Systems: Principles and Paradigms (3rd ed.). Pearson.
  7. Coulouris, G., Dollimore, J., Kindberg, T., & Blair, G. (2011). Distributed Systems: Concepts and Design (5th ed.). Addison-Wesley.
  8. Chandra, T.D., & Toueg, S. (1996). Unreliable failure detectors for reliable distributed systems. Journal of the ACM, 43(2), 225–267.

思考题

  1. CAP 定理指出在网络分区(P)时,系统只能在一致性(C)和可用性(A)之间选择。但在没有网络分区时,系统可以同时满足 CA。PACELC 定理扩展了 CAP——即使没有分区,系统仍然需要在延迟(L)和一致性(C)之间权衡。在你设计分布式系统时,PACELC 比 CAP 更有指导意义吗?
  2. 一致性模型的谱系:线性一致性(最强)→ 顺序一致性 → 因果一致性 → 最终一致性(最弱)。etcd 提供线性一致性,DynamoDB 提供最终一致性(可选强一致性读)。在不同的业务场景中,你如何判断需要什么级别的一致性?‘过度’的一致性保证会带来什么代价?
  3. FLP 不可能性定理证明了在异步分布式系统中,即使只有一个进程可能崩溃,也不可能达成确定性的共识。Paxos 和 Raft 通过引入’超时’和’领导者选举’来绕过 FLP——但这依赖于’部分同步’假设。在什么网络条件下这些算法可能失效(如极端的网络延迟导致频繁选举)?