本文是 DDIA 第二部分的完整读书笔记,涵盖第 5-9 章:数据复制、数据分区、事务、分布式挑战、一致性与共识。
第5章:数据复制
5.1 复制的目的
| 目的 |
说明 |
| 高可用性 |
部分节点故障时系统仍可用 |
| 降低延迟 |
将数据放在离用户更近的地方 |
| 提高读吞吐 |
多个副本可以并行处理读请求 |
5.2 主从复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ┌──────────────────────────────────────────────┐ │ 主从复制架构 │ ├──────────────────────────────────────────────┤ │ 客户端写入 │ │ │ │ │ ▼ │ │ ┌────────┐ 复制日志 ┌────────┐ │ │ │ 主节点 │ ─────────> │ 从节点1 │ │ │ │(Leader)│ │(Follower)│ │ │ └────────┘ └────────┘ │ │ │ │ │ │ │ 复制日志 │ │ │ ▼ ▼ │ │ ┌────────┐ ┌────────┐ │ │ │ 从节点2 │ │ 从节点3 │ │ │ └────────┘ └────────┘ │ │ │ │ 客户端可以从任意节点读取 │ └──────────────────────────────────────────────┘
|
同步复制 vs 异步复制
| 方式 |
优点 |
缺点 |
| 同步 |
数据一致,从节点数据最新 |
延迟高,从节点故障会阻塞 |
| 异步 |
延迟低,不受从节点影响 |
数据可能丢失 |
| 半同步 |
平衡一致性和可用性 |
实现复杂 |
故障转移(Failover)
1 2 3 4 5
| 1. 检测主节点故障(心跳超时) 2. 选举新主节点(选择数据最新的节点) 3. 重新配置系统 - 客户端发送写请求到新主节点 - 其他从节点从新主节点复制
|
问题:数据丢失、脑裂、超时设置
5.3 复制延迟问题
读己之写一致性:用户写入后立即读取可能看到旧数据
单调读:用户刷新页面可能路由到不同从节点,看到数据”回退”
一致前缀读:因果关系打乱(先看到回答再看到问题)
5.4 多主复制
1 2 3 4
| ┌──────────┐ ┌──────────┐ │ 数据中心1 │ <─────> │ 数据中心2 │ │ (主节点1) │ 异步 │ (主节点2) │ └──────────┘ └──────────┘
|
冲突解决策略:
- 最后写入胜出(LWW)
- 合并值
- CRDT
- 自定义逻辑
5.5 无主复制(Quorum)
公式:W + R > N
| 符号 |
含义 |
| N |
副本总数 |
| W |
写入需要确认的副本数 |
| R |
读取需要查询的副本数 |
1 2 3 4
| 示例:N=3, W=2, R=2 写入成功需要 2 个副本确认 读取需要查询 2 个副本 W + R = 4 > N = 3 → 保证读到最新值
|
第6章:数据分区
6.1 分区的目的
将大数据集分散到多个节点,每个节点只存储部分数据
6.2 分区策略
按键范围分区
1
| 分区1: A-F 分区2: G-N 分区3: O-Z
|
优点:支持范围查询
缺点:可能导致热点
按哈希分区
优点:均匀分布
缺点:不支持范围查询
6.3 次级索引分区
| 类型 |
特点 |
| 本地索引 |
写入快,查询需要scatter-gather |
| 全局索引 |
查询快,写入需要分布式事务 |
6.4 分区再平衡
| 策略 |
说明 |
| 固定分区数 |
预先创建多个分区 |
| 动态分区 |
根据数据大小自动分裂/合并 |
| 按节点分区 |
每个节点固定数量的分区 |
第7章:事务
7.1 ACID 特性
| 特性 |
说明 |
| 原子性 (Atomicity) |
全部成功或全部失败 |
| 一致性 (Consistency) |
从一个有效状态到另一个有效状态 |
| 隔离性 (Isolation) |
并发事务互不干扰 |
| 持久性 (Durability) |
提交后数据不丢失 |
7.2 隔离级别
| 级别 |
脏读 |
不可重复读 |
幻读 |
| 读未提交 |
✓ |
✓ |
✓ |
| 读已提交 |
✗ |
✓ |
✓ |
| 可重复读 |
✗ |
✗ |
✓ |
| 可串行化 |
✗ |
✗ |
✗ |
7.3 串行化实现
真正的串行执行
两阶段锁定(2PL)
可串行化快照隔离(SSI)
7.4 分布式事务
两阶段提交(2PC)
1 2 3 4 5 6 7
| 阶段1(准备): 协调者 ──准备?──> 所有参与者 <──准备好──
阶段2(提交): 协调者 ──提交──> 所有参与者 <──完成──
|
问题:协调者故障时参与者阻塞
第8章:分布式系统的挑战
8.1 不可靠的网络
- 请求可能丢失
- 请求可能排队很久
- 响应可能丢失
- 无法区分节点故障和网络故障
8.2 不可靠的时钟
| 时钟类型 |
用途 |
| 墙上时钟 |
获取当前时间(NTP同步) |
| 单调时钟 |
测量时间间隔 |
问题:不同机器时钟可能不同步
8.3 进程暂停
8.4 拜占庭故障
节点可能发送任意消息(故意或无意)
第9章:一致性与共识
9.1 一致性模型
1 2 3
| 弱 ←─────────────────────────────────────→ 强
最终一致性 因果一致性 顺序一致性 线性一致性
|
9.2 线性一致性
系统表现得好像只有一个数据副本,所有操作都是原子的
1 2 3 4 5 6 7 8
| 时间线: ────────────────────────────────────────>
客户端A: ├── 写入 x=1 ──┤ 客户端B: ├── 读取 x ──┤ → 必须返回 1 客户端C: ├── 读取 x ──┤ → 必须返回 1
一旦任何客户端读到新值,所有后续读取都必须返回新值
|
9.3 CAP 定理
1 2 3 4 5 6 7 8 9 10 11
| C (Consistency) ╱╲ ╱ ╲ ╱ 只能 ╲ ╱ 选择2个 ╲ A ────────── P (Availability) (Partition Tolerance)
网络分区时必须选择: - CP:保证一致性,牺牲可用性 - AP:保证可用性,牺牲一致性
|
9.4 共识算法
共识的性质:
| 性质 |
说明 |
| 一致同意 |
所有节点决定相同的值 |
| 完整性 |
决定的值必须是某个节点提议的 |
| 终止性 |
最终会做出决定 |
9.5 Raft 算法
1 2 3 4 5 6 7 8 9 10 11
| ┌──────────┐ │ 领导者 │ ← 处理所有客户端请求 │ (Leader) │ └──────────┘ │ │ 复制日志 ▼ ┌──────────┐ ┌──────────┐ │ 跟随者1 │ │ 跟随者2 │ │(Follower)│ │(Follower)│ └──────────┘ └──────────┘
|
选举流程:
- 跟随者超时未收到心跳
- 转变为候选人,增加任期
- 向其他节点请求投票
- 获得多数票则成为领导者
9.6 Paxos 算法
1 2 3 4 5 6 7
| 阶段1(Prepare): 提议者 ──Prepare(n)──> 接受者 <──Promise──
阶段2(Accept): 提议者 ──Accept(n,v)──> 接受者 <──Accepted──
|
Raft vs Paxos
| 方面 |
Raft |
Paxos |
| 可理解性 |
高 |
低 |
| 领导者 |
稳定领导者 |
每次共识可能不同 |
| 日志 |
连续日志 |
可能有空洞 |
9.7 共识的应用
- ZooKeeper:Kafka 使用进行领导者选举
- etcd:Kubernetes 存储集群状态
- Consul:服务发现和配置
本章关键要点
- 复制的核心挑战是处理变更
- 异步复制有数据丢失风险
- Quorum 使用 W + R > N 保证一致性
- 分区支持水平扩展
- 隔离级别是性能与一致性的权衡
- 线性一致性是最强的一致性保证
- CAP 定理表明网络分区时需要取舍
- Raft 比 Paxos 更易理解
延伸阅读
- 《Amazon Dynamo》论文:无主复制经典论文
- 《Paxos Made Simple》:Lamport 的简化版 Paxos
- 《In Search of an Understandable Consensus Algorithm》:Raft 论文