Skip to content

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

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

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

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 同步。

问题:

  • Clock Drift (ppm 级别的误差)
  • NTP 跳变 (校时的时候会跳过去)
  • Leap Second
  • 数据中心之间有 ms 到几十 ms 的误差

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]
  • 可以判断 A 与 B 是并发的,还是谁在前
  • 体积问题: 节点一多,元数据就变大

2.4 HLC (Hybrid Logical Clock, 2014)

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

HLC = (physical_time, logical_counter)
  • 以物理时钟为基准排序 + 用逻辑时钟保留因果
  • CockroachDB、YugabyteDB 等现代分布式 DB 的标准做法

2.5 TrueTime (Google Spanner)

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

  • Commit Wait: 等过不确定区间(ms)之后再完成
  • 强一致性 + 性能 (Linearizable)
  • 普通企业实现不了 (需要硬件投入)

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

3.1 主要的一致性模型

由强到弱:

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

3.2 重新审视 CAP

  • Consistency: Linearizability
  • Availability: 每个请求都能拿到回应
  • Partition Tolerance: 网络分区时仍能工作

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

3.3 PACELC (更贴近现实)

  • Partition: C or A
  • Else (正常运行时): L(atency) or C(onsistency)

大多数系统是 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 提出的鼻祖。正确,却以难懂难实现著称。

角色:

  • Proposer (提出值)
  • Acceptor (投票)
  • Learner (学习决定)

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

4.4 Raft (2014)

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

3 个核心:

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

状态:

  • Follower → Candidate (Election Timeout) → Leader (过半数投票)
  • Leader 的 heartbeat 失败时 → 重新选举

使用场景: 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 的传播延迟会带来这些问题:

  • Read-your-writes: 刚写完的东西读不到
  • Monotonic reads: 之前看到过的东西又不见了
  • Consistent Prefix: 看到了没有原因的结果

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

5.3 Conflict Resolution (Multi-Leader/Leaderless)

  • LWW (Last Writer Wins): 简单,有丢数据的风险
  • Version Vectors: 准确,复杂
  • Application-level Merge: 用户自定义 (CRDT)

第 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 实战使用场景

  • Redis CRDT (Enterprise): Active-Active Geo
  • Riak: Dynamo + CRDT
  • Automerge (Ink & Switch): 协作编辑器
  • Yjs: Real-time collaboration (Notion、Linear 的灵感来源)
  • Figma: 自研 CRDT 用于设计协作

6.5 局限

  • 难以表达任意不变式 (例如唯一性)
  • 元数据体积 (Tombstone)
  • 会丢掉操作顺序的语义 (这既是优点也是缺点)

第 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

优点:

  • 完整的历史 (审计与调试)
  • 时间旅行
  • 通过重放事件生成新的 View
  • 对分布式友好 (事件是 append-only)

缺点:

  • 复杂度 ↑
  • 模式演进的问题 (如何解释旧事件)
  • 没有 Snapshot 的话重放很慢

7.2 CQRS (Command Query Responsibility Segregation)

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

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

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

7.3 Event Sourcing + CQRS 技术栈

  • EventStoreDB: 专用 DB
  • Kafka + Kafka Streams: 事件日志 + Projection
  • Axon Framework (Java): 一体化
  • MartenDB (.NET): 构建在 Postgres 之上

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

适合的时候:

  • 金融、交易这类可审计性很重要的场景
  • 复杂业务逻辑 + 需要重建过去
  • 以事件为中心的架构

该避开的时候:

  • 简单 CRUD
  • 团队里新手居多
  • 一致性要求很简单

第 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 实现工具

  • Temporal: 工作流引擎。Saga 的事实标准。
  • AWS Step Functions: 托管服务
  • Camunda: 基于 BPMN
  • Cadence (Uber,Temporal 的前身): 开源

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

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

  • Chaos Monkey (Netflix 2011): 随机 kill 实例
  • Chaos Mesh: K8s 原生
  • LitmusChaos: 开源
  • Gremlin: 商用

原则:

  • 在生产环境做 (一开始先在预发)
  • 限制 Blast Radius
  • 自动回滚
  • 定期执行 (Game Day)

第 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: 理论基础

  • 精读 DDIA (Designing Data-Intensive Applications)
  • Lamport 的两篇 Paper
  • 8 Fallacies 思想实验

Month 2: Consensus 实操

  • 亲手实现 Raft (MIT 6.824)
  • 读 etcd、Consul 的内部实现
  • 理解 Kafka KRaft 的结构

Month 3: Replication + Consistency

  • 练习区分各种一致性模型
  • CockroachDB、Spanner 的白皮书
  • 实现 TrueTime 与 HLC

Month 4: Event Sourcing + Saga

  • 用 Event Sourcing 做一个迷你银行系统
  • 用 Temporal 实现 Saga
  • 把 Outbox 模式真正落地

Month 5: CRDT + 实时

  • 学习 Automerge、Yjs
  • 做一个协作编辑器原型
  • 精读 Figma 的技术博客

Month 6: 故障 + 运维

  • 引入 Chaos Monkey
  • 实现主要故障模式的防御
  • 分析 10 份 Incident Postmortem

第 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 进阶。下一篇包括:

  • B-Tree、LSM-Tree、Hash Index 的内部结构
  • 深入读懂查询规划器与 EXPLAIN
  • 事务隔离级别 (Read Uncommitted 到 Serializable)
  • 分片与分区策略
  • PostgreSQL 的一枝独秀 (2025)
  • Vector DB 与 pgvector、Qdrant、Weaviate

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

현재 단락 (1/284)

Butler Lampson 有一句著名的定义:

작성 글자: 0원문 글자: 8,775작성 단락: 0/284