LabHub

博客

分布式系统完全指南 — 时钟、Consensus、Event Sourcing、Saga、CRDT、故障模式 (Season 2 Ep 12, 2025)

한국어English日本語中文

前言 — 分布式系统之所以难的真正原因

Butler Lampson 有一句著名的定义:

“分布式系统,就是你甚至不知道它存在的那台计算机出了故障,却让你自己的计算机没法用了。”

分布式系统之所以难,是因为下面这 3 件事会同时发生:

  1. 时钟不一致 (Clock Drift)
  2. 故障是局部的 (Partial Failure)
  3. 网络必须被怀疑 (Unreliable Network)

本文把针对这 3 个问题的理论工具(时钟、共识、一致性)与实战模式(Event Sourcing、Saga、CRDT)一次性梳理清楚。


第 1 部 — 8 Fallacies of Distributed Computing

Peter Deutsch 在 1994 年归纳的分布式系统 8 个错误假设:

  1. 网络是可靠的
  2. 延迟为零
  3. 带宽是无限的
  4. 网络是安全的
  5. 拓扑不会改变
  6. 管理员只有一位
  7. 传输成本为零
  8. 网络是同质的

任何违背这 8 条的假设,早晚都会在生产环境炸掉。


第 2 部 — 时间的 3 种模型

2.1 Physical Clock (物理时钟)

time.Now() — 操作系统的时钟。通过 NTP 同步。

问题:

2.2 Logical Clock (逻辑时钟)

Lamport Timestamp (1978):

事件发生时 counter++
消息发送: 发送 (event, counter)
消息接收: counter = max(local, received) + 1

优点: 保留因果关系 (happens-before) 缺点: 无法区分两者之间到底有没有因果关系 (concurrent vs causal)

2.3 Vector Clock

长度等于节点数 N 的向量:

A: [2, 1, 0]
B: [1, 3, 0]

2.4 HLC (Hybrid Logical Clock, 2014)

物理时间 + 逻辑计数器 的组合:

HLC = (physical_time, logical_counter)

2.5 TrueTime (Google Spanner)

用 GPS 与原子钟保证 ±7ms → 于是“这个时刻之后的事件一定在后面”成为可能。


第 3 部 — 一致性模型的谱系

3.1 主要的一致性模型

由强到弱:

  1. Linearizable: 所有操作都按实时顺序呈现
  2. Sequential: 所有进程看到的顺序相同,但不是实时顺序
  3. Causal: 只对有因果关系的操作保证顺序
  4. Eventual: 最终会一致

3.2 重新审视 CAP

网络分区是躲不掉的 → 所以要在 CP 与 AP 之间选。

3.3 PACELC (更贴近现实)

大多数系统是 PA/EL (分区时保可用 + 正常时保低延迟)。

3.4 2025 年 DB 的一致性选择

DB模型
PostgreSQL单节点 Serializable
MySQLRead Committed (默认)
SpannerLinearizable + External Consistency
CockroachDBSerializable + Causal
CassandraTunable (Quorum、Eventual)
DynamoDBEventual (可选 Strong)
Redis Cluster单键 Linearizable,多键 ❌

第 4 部 — Consensus: 共识算法

4.1 为什么需要 Consensus

分散的多个节点要对同一个值达成一致 — 领导者选举、状态复制、事务提交等等。

4.2 FLP Impossibility (1985)

在异步系统中,能容忍哪怕 1 个节点故障的 deterministic consensus 是不可能的。

真实系统要么依赖时序假设 (eventually synchronous),要么走概率性路线。

4.3 Paxos (1989)

Leslie Lamport 提出的鼻祖。正确,却以难懂难实现著称。

角色:

问题: 实战中不好用,于是有了 Multi-Paxos、Fast Paxos、EPaxos 等变体。

4.4 Raft (2014)

在 Stanford 以“可理解的 Consensus”为目标设计。如今是事实标准。

3 个核心:

  1. Leader Election: 选出唯一的领导者
  2. Log Replication: 领导者把日志复制给 Follower
  3. Safety: 已 Committed 的日志不会被推翻

状态:

使用场景: etcd, Consul, TiKV, CockroachDB, Kafka KRaft (2024 年起)。

4.5 PBFT (Practical Byzantine Fault Tolerance, 1999)

在存在恶意节点时,3f+1 个节点中可容忍 f 个故障。用于区块链。

阶段: Pre-prepare → Prepare → Commit。消息复杂度 O(N²)。

4.6 Tendermint、HotStuff (2018 年起)

把 BFT 按区块链的需要现代化。Libra/Diem 的 HotStuff,Cosmos 的 Tendermint。

4.7 Nakamoto Consensus

Bitcoin 的 Proof-of-Work。概率性共识 — 只要后面压了足够多的区块就算 confirmed。

缺点: 耗能、慢。所以 Ethereum 转向了 PoS。


第 5 部 — Replication

5.1 4 种复制模式

  1. Single-Leader: 写走领导者,读随处可读。简单。大多数 RDBMS。
  2. Multi-Leader: 多个领导者都接受写。需要冲突解决。用得很少。
  3. Leaderless: 所有节点对等。Dynamo-style。Cassandra, Riak, DynamoDB。
  4. Quorum-based: 只要 R + W > N 就有强一致性。

5.2 Replication Lag

Leader → Follower 的传播延迟会带来这些问题:

解决: 从 Leader 读、Sticky Session、基于 Timestamp 的一致性。

5.3 Conflict Resolution (Multi-Leader/Leaderless)


第 6 部 — CRDT (Conflict-free Replicated Data Type)

6.1 什么是 CRDT

一种在数学上不可能冲突的数据结构。无论按什么顺序合并,结果都一样。

6.2 两种类型

State-based (CvRDT): 传输状态本身 + merge Operation-based (CmRDT): 传输操作 + 重放

6.3 有代表性的 CRDT

CRDT用途
G-Counter只增 (计数器)
PN-Counter可增可减
G-Set只能添加
OR-Set添加与删除
LWW-Register最后写入者胜出
RGA有序列表 (文本编辑)
JSON CRDT (Automerge)嵌套结构

6.4 实战使用场景

6.5 局限


第 7 部 — Event Sourcing + CQRS

7.1 Event Sourcing

保存事件而不是状态。状态由重放事件推导出来。

events = [
  AccountCreated(id=1, balance=0),
  MoneyDeposited(account=1, amount=100),
  MoneyWithdrawn(account=1, amount=30),
]

balance = fold(events, 0, apply) // 70

优点:

缺点:

7.2 CQRS (Command Query Responsibility Segregation)

写模型(Command) ≠ 读模型(Query) 分开。

Write: events → EventStore
Projection
Read: Materialized View (已优化)

优点: 读与写可以独立优化、独立扩展。 缺点: 一致性有延迟 (Eventual)。

7.3 Event Sourcing + CQRS 技术栈

7.4 什么时候用,什么时候避开

适合的时候:

该避开的时候:


第 8 部 — Saga 模式

8.1 分布式事务的问题

2PC (Two-Phase Commit): 理论上完美,但会block,而且经不起 Coordinator 失败。如今几乎不用了。

替代方案: Saga — 一串本地事务 + 失败时执行补偿事务。

8.2 Choreography (协同)

各个服务发布 + 订阅事件:

OrderCreatedInventoryReservedPaymentChargedOrderConfirmed
                  ↓ 失败
              InventoryReleasedOrderCancelled (补偿)

优点: 服务之间的耦合度低。 缺点: 流程难以追踪。

8.3 Orchestration (编排)

由中央的 Orchestrator 显式管理流程:

Orchestrator:
  1. ReserveInventory
  2. ChargePayment
  3. ShipOrder
  失败时 ← CompensateAll

优点: 流程显式,调试容易。 缺点: Orchestrator 可能变成单点故障。

8.4 实现工具

8.5 Temporal 示例

func ProcessOrder(ctx workflow.Context, order Order) error {
    err := workflow.ExecuteActivity(ctx, ReserveInventory, order).Get(ctx, nil)
    if err != nil { return err }
    
    err = workflow.ExecuteActivity(ctx, ChargePayment, order).Get(ctx, nil)
    if err != nil {
        workflow.ExecuteActivity(ctx, ReleaseInventory, order)
        return err
    }
    // ...
}

Temporal 免费提供了重启安全性、定时器与可视化


第 9 部 — 故障模式 12 种

9.1 故障传播模式

  1. Cascading Failure: 一个服务变慢 → 调用方超时堆积 → 传播开来
  2. Thundering Herd: 缓存过期时所有请求一起涌向 Origin
  3. Retry Storm: 失败的请求被重试放大
  4. Metastable Failure: 临时性问题在恢复正常之后依然持续
  5. Split-Brain: 网络分区导致出现两个领导者
  6. Clock Skew Bug: 时钟差异让时间戳发生倒转
  7. Gray Failure: 没死,但功能已经劣化
  8. Poison Pill: 某条特定消息把所有 Consumer 都搞挂
  9. Queue Backlog: Consumer 太慢,队列无限堆积
  10. Hot Partition: 流量集中到某一个分片上
  11. Fan-out Overload: 一个请求变成 100 个内部请求
  12. Noisy Neighbor: 共享资源上一个租户影响所有人

9.2 防御模式

故障防御
CascadingCircuit Breaker, Bulkhead, Timeout
Thundering HerdRequest Coalescing, Jittered refresh
Retry StormExponential Backoff + Jitter
MetastableLoad Shedding, 限制 Queue Depth
Split-BrainFencing Token, Quorum
Clock SkewHLC, 监控 NTP
Gray FailureHealth Check + 积极摘除
Poison PillDead Letter Queue + Alert
Queue BacklogBackpressure, Auto-scale
Hot PartitionConsistent Hashing + replication
Fan-out考虑 Timeout 的层级深度, Async
Noisy NeighborResource Quota, QoS

9.3 Chaos Engineering

有意注入故障 → 验证系统。

原则:


第 10 部 — 分布式系统模式 10 条

  1. Idempotency: 同一个请求发 N 次 = 1 次的效果
  2. Exactly-once is a lie: At-least-once + Idempotent Consumer
  3. Outbox Pattern: DB 事务与事件发布的原子性
  4. Inbox Pattern: 幂等 Consumer 的实现方式
  5. Circuit Breaker: 检测失败 + 绕开
  6. Bulkhead: 用线程池隔离防止连锁失败
  7. Leader Election: 基于 Lease
  8. Sharding: 数据切分
  9. Sidecar: 把通用功能 (日志、安全) 放进独立容器
  10. Service Mesh: 把 Sidecar 提升成网络层

第 11 部 — 六个月分布式系统路线图

Month 1: 理论基础

Month 2: Consensus 实操

Month 3: Replication + Consistency

Month 4: Event Sourcing + Saga

Month 5: CRDT + 实时

Month 6: 故障 + 运维


第 12 部 — 分布式系统检查清单 12 条

  1. 能说出 8 Fallacies
  2. 知道 Lamport vs Vector vs HLC 的区别
  3. 知道 CAP vs PACELC 的区别
  4. 能解释 Raft 的 3 个核心
  5. 知道 FLP Impossibility 的含义
  6. 知道 Single-Leader vs Leaderless 的取舍
  7. 知道 CRDT 要解决的问题
  8. 知道 Event Sourcing + CQRS 的优缺点
  9. 知道 Saga Choreography vs Orchestration 的区别
  10. 能说出 防御 Cascading Failure 的 3 种手段
  11. 知道 Outbox 与 Inbox 模式 的作用
  12. 知道 Chaos Engineering 的原则

第 13 部 — 分布式系统反模式 10 条

  1. “网络是可靠的”: 8 Fallacies 的第 1 条。Timeout 与重试是必须的
  2. 把 2PC 用到微服务里: 有 Block 的风险。改用 Saga
  3. 跨 Kafka 分区假定事件顺序: 只在分区内部才有保证
  4. 不用 DB 事务就想要 Exactly-once: 不可能。用 Outbox 或者幂等
  5. 信任 Clock: 用 time.Now() 排序 → 出 bug。逻辑时钟是必须的
  6. 无限重试: 会引发 Retry Storm。要用 Backoff + Circuit Breaker
  7. 假定单节点 DB 加复制就安全了: 必须把 Split-Brain 考虑进去
  8. Consumer 失败时不 NACK 而是阻塞: DLQ 的设计是必须的
  9. 全局事务: 性能与可用性 ↓。重新设计边界
  10. “Eventual Consistency 最终会一致”: 从用户视角看仍然是问题。要用 UX 解决

结语 — 分布式系统是“脑子里的模型”

分布式系统是直觉最常背叛你的领域。“直觉很强”的工程师最危险。

需要的是:

2025 年资深工程师的分布式能力,是把年薪拉开两倍的那根轴。这个领域有多难,就有多值钱。


下一篇预告 — “DB 完全指南: 内部结构、索引、查询规划器、分区、Vector DB”

Season 2 Ep 13 讲数据的心脏,也就是DB 进阶。下一篇包括:

DB 究竟是怎么处理查询的,下一篇见。

评论

还没有评论。

登录后即可发表评论