事务与恢复
0. 引言
事务的 ACID 中,原子性要求事务要么全部生效要么全部不生效,持久性要求一旦提交结果永久可见——崩溃恢复机制正是为实现这两点而存在。本文从单机存储的 WAL 与恢复策略讲起,经过隔离级别与并发控制,最后上升到分布式事务的原子提交协议与两种代表性路线(Spanner 与 Calvin)。
1. 本地恢复:WAL 与检查点
1.1 预写日志(WAL)
WAL 的核心原则:对数据的任何修改,必须先写入日志并落盘,再修改内存中的页。
写入流程:
1. 将修改记录追加到 WAL,fsync 落盘
2. 在内存中应用修改(脏页)
3. 脏页由后台异步刷盘崩溃后,数据库重放(Replay)WAL 中已提交的事务,撤销(Undo)未提交的事务,从而恢复到一致状态。
1.2 检查点(Checkpoint)
若每次恢复都从头重放全部 WAL,恢复时间会随日志增长而变长。检查点机制定期将内存中的脏页刷盘,并记录"已持久化到何处",恢复时只需从最近检查点之后的日志开始重放。
1.3 缓冲与刷盘策略
| 策略 | 行为 | 权衡 |
|---|---|---|
| Force | 事务提交时强制刷盘所有脏页 | 恢复快但写入慢 |
| No-Force | 依赖 WAL,提交时只保证 WAL 落盘,脏页异步刷 | 写入快 |
| Steal / No-Steal | 未提交事务的脏页是否允许提前刷盘 | 影响 Undo 日志的设计 |
现代系统普遍采用 No-Force + Steal + WAL,兼顾性能与可恢复性。
2. 隔离级别与并发控制
2.1 四级隔离
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(RU) | 可能 | 可能 | 可能 |
| 读已提交(RC) | 否 | 可能 | 可能 |
| 可重复读(RR) | 否 | 否 | 可能 |
| 串行化(Serializable) | 否 | 否 | 否 |
2.2 三种并发控制机制
| 机制 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 两阶段锁(2PL) | 事务分为加锁与解锁两阶段,不交叉 | 可实现串行化 | 读写互斥,易死锁,需死锁检测/超时回滚 |
| 多版本并发控制(MVCC) | 每次写生成新版本,读基于快照访问特定版本 | 读不加锁,读多场景性能好 | 需维护多版本与垃圾回收(Vacuum) |
| 乐观并发控制(OCC) | 执行时不加锁,提交时校验冲突,冲突则回滚重试 | 低争用下吞吐高 | 高争用下大量回滚 |
2.3 分布式下的并发控制
跨节点的并发控制需结合全局事务与复制:
- 通过原子提交协议保证跨节点原子性(见第 3 节);
- 通过全局时间戳或共识算法确定全局有序的提交顺序——如 Spanner 的 TrueTime、Calvin 的全局排序。
3. 分布式原子提交协议
3.1 两阶段提交(2PC)
2PC 是最经典的原子提交协议,引入协调者(Coordinator)统一决策:
- 阶段一(Prepare):协调者向所有参与者发送 Prepare;参与者写 Undo/Redo 日志、锁定资源,回复 Yes/No;
- 阶段二(Commit):全部 Yes → 协调者发 Commit,参与者提交并释放锁;任一 No → 协调者发 Rollback,参与者回滚。
缺点:阻塞——协调者故障后参与者持有锁等待;单点——协调者是瓶颈与单点。
3.2 三阶段提交(3PC)
在 2PC 的准备与提交间增加**预提交(Pre-Commit)**阶段,并引入超时机制:
- 优点:参与者超时后可自主决策,减少无限阻塞;
- 缺点:网络分区下仍可能不一致,实现复杂,实践中少用。
3.3 基于 Paxos 的原子提交
将提交决策本身放入共识:协调者的决策通过 Paxos / Raft 复制,即使协调者崩溃,新协调者也能从已复制的日志恢复决策,避免 2PC 的阻塞单点。代表如 Spanner 用 Paxos 复制事务状态;关键点:提交记录一旦在多数派达成,即不可逆。
本地与分布式的关系:参与者的准备阶段依赖 WAL——写日志落盘后再回复 Yes,保证崩溃后可重放决策。
4. 两条分布式事务路线:Spanner 与 Calvin
4.1 Spanner:TrueTime + 2PC
Spanner 是 Google 的全球分布式数据库,通过以下组合实现外部一致(线性一致)的分布式事务:
- TrueTime API:基于 GPS + 原子钟提供带有不确定区间的时间戳
TT(now) = [earliest, latest]; - 提交时间戳:协调者选择
timestamp > 已见最大 TT.now().latest,保证事务顺序与真实时间一致; - 2PC + Paxos:每个分片的副本通过 Paxos 复制,2PC 在分片间提交。
特点:线性一致的跨节点事务,但依赖专用硬件(原子钟)校准时钟。
4.2 Calvin:确定性调度
Calvin 走另一条路——在事务执行前,先通过共识就事务的全局顺序达成一致,再确定性地顺序执行:
- 全局顺序:所有节点按同一顺序执行事务日志;
- 确定性执行:相同输入 + 相同顺序 → 相同结果,无需在执行时协调;
- 优势:避免 2PC 的运行时交互,吞吐高;
- 局限:需预先确定事务(只读事务需特殊处理),不适合高度交互式负载。
4.3 对比
| 维度 | Spanner | Calvin |
|---|---|---|
| 顺序来源 | TrueTime 真实时钟 | 共识确定的逻辑顺序 |
| 执行方式 | 并发 + 锁 + 2PC | 确定性顺序执行 |
| 一致性 | 外部一致(线性) | 串行化 |
| 硬件依赖 | 原子钟 / GPS | 无 |
| 适用 | 地理分布、强一致 | 高吞吐、可预测事务 |
5. 小结
- 本地恢复三件套:WAL(先日志后数据)+ 检查点(缩短恢复)+ No-Force/Steal(性能与可恢复性兼得);
- 并发控制:2PL(强一致易死锁)、MVCC(读不阻塞)、OCC(低争用高效);
- 分布式原子提交演进:2PC(阻塞/单点)→ 3PC(超时自决策)→ Paxos 化(决策复制,不阻塞);
- 两条路线:Spanner 用真实时钟排序 + 2PC/Paxos,Calvin 用确定性顺序执行避免运行时协调。
下一章讲解分布式协调:共识算法、领导者选举与协调服务。