日志与恢复
这份讲义基于 CMU Database Systems 课程关于**日志与恢复(Logging & Recovery)**的内容(Lecture 21 & 22)整理而成。这部分知识是数据库实现 ACID 中 原子性(Atomicity) 和 持久性(Durability) 的基石。
第一章:故障恢复概述与缓冲池策略 (Crash Recovery & Buffer Pool Policies)
1. 核心概念解释
- 故障恢复的目标:
- 确保在系统崩溃(如断电、OS 崩溃)后,所有**已提交(Committed)**的事务更改是持久的(Durability)。
- 所有**未提交(Aborted/Incomplete)**的事务更改被回滚,保证原子性(Atomicity),。
- 缓冲池策略 (Buffer Pool Policies):决定何时将内存中的脏页(Dirty Pages)写入磁盘,。
- Steal (窃取):允许在事务提交前,将其修改的脏页写回磁盘(即覆盖磁盘上的旧数据)。
- Force (强制):强制在事务提交时刻,将其所有修改的页立即写回磁盘。
2. 关键结论 / 方法
- 策略组合与恢复机制:
- No-Steal + Force:最简单。无需 Undo(因为未提交数据不写盘),无需 Redo(因为提交时数据已强刷盘)。但性能最差。
- Steal + No-Force:现代数据库首选策略。性能最好,但实现最复杂。
- 需要 Undo:因为崩溃时磁盘上可能包含未提交事务的数据(Steal 导致)。
- 需要 Redo:因为崩溃时已提交事务的数据可能还在内存中未刷盘(No-Force 导致)。
3. 易混点提示
- 为什么不使用 Force? 虽然 Force 让恢复变简单(不需要 Redo),但会导致严重的 I/O 性能问题(随机写),因为每次提交都必须等待磁盘写入完成。
- 为什么使用 Steal? 如果使用 No-Steal,缓冲池必须足够大以容纳当前所有活跃事务修改的页,否则事务无法继续执行。
4. 复习小结
数据库通常采用 Steal + No-Force 策略以获得最佳运行时性能,但这要求恢复算法必须同时支持 Undo(回滚未提交数据)和 Redo(重做已提交数据)。
第二章:影子分页与预写日志 (Shadow Paging & WAL)
1. 核心概念解释
- 影子分页 (Shadow Paging):
- 维护两个数据库版本:Master(当前一致状态)和 Shadow(正在修改的状态)。
- 事务修改时,复制页面到新位置(Shadow),不覆盖原处。
- 提交时,原子地切换 Master 指针指向 Shadow 页表。
- 预写日志 (Write-Ahead Logging, WAL):
- 维护一个单独的日志文件,记录所有操作。
- 核心规则:内存中的脏页在写回磁盘前,必须确保其对应的日志记录已安全写入磁盘。
2. 关键结论 / 方法
- WAL 协议流程,:
- 事务修改数据前,先在内存生成日志记录。
- 事务提交时,将日志缓冲区(Log Buffer)刷入磁盘(Flush)。只有日志落盘后,才算提交成功。
- 数据页可以稍后(异步)刷盘。
- 日志记录内容格式:
- Physical Logging:记录字节级的差异(Diff)。
- Logical Logging:记录 SQL 语句。恢复慢(需重新执行查询)。
- Physiological Logging:最常用。记录“在页面 X 的 Slot Y 做了操作 Z”。允许页面内数据重组,且恢复速度快。
3. 易混点提示
- Shadow Paging 的缺陷:虽然实现简单(无需 Undo/Redo),但会导致数据碎片化(Fragmentation),将顺序写变为随机写,且复制页面的开销大。
- Group Commit (组提交):为了缓解每次提交都刷日志的开销,系统会等待多个事务提交请求或超时(如 5ms),批量刷盘。
4. 复习小结
Shadow Paging 已被主流数据库淘汰(除特定场景如 LMDB)。WAL 是标准解决方案,它通过将随机数据页写入转化为顺序日志写入来提高性能,同时保证了故障恢复能力。
第三章:检查点机制 (Checkpoints)
1. 核心概念解释
- 检查点 (Checkpoint):为了解决日志无限增长导致恢复时间过长的问题,定期将内存中的脏页刷入磁盘,并记录“检查点”日志,。
- 一致性检查点 (Blocking Checkpoint):
- 暂停所有事务 -> 刷出所有脏页 -> 写入检查点日志 -> 恢复事务。
- 模糊检查点 (Fuzzy Checkpoint):
- 允许在做检查点时事务继续运行。需记录 活跃事务表 (ATT) 和 脏页表 (DPT)。
2. 关键结论 / 方法
- 一致性检查点的问题:会造成系统长时间停顿(Stop-the-world),用户体验差。
- 模糊检查点的数据结构:
- Active Transaction Table (ATT):记录检查点开始时活跃的事务及其状态。
- Dirty Page Table (DPT):记录所有脏页及其
RecLSN(该页最早一次未刷盘修改对应的日志序列号)。
3. 易混点提示
- 检查点不是“清空”日志:检查点只是告诉恢复算法,在检查点之前的数据已经落盘,可以缩短扫描范围,并不意味着可以立即删除旧日志(取决于最早的未提交事务)。
4. 复习小结
简单的检查点需要阻塞系统,不可取。现代系统(如 ARIES)使用模糊检查点,通过记录 ATT 和 DPT 来避免阻塞,虽然实现复杂但对性能影响小。
第四章:ARIES 恢复算法 (ARIES Recovery Algorithm)
1. 核心概念解释
- ARIES:IBM 提出的现代数据库恢复算法黄金标准。
- 关键数据结构-:
- LSN (Log Sequence Number):全局唯一单调递增的日志序列号,代表物理时间顺序。
- PageLSN:每个数据页记录最后修改它的日志记录 LSN。
- FlushedLSN:磁盘上最新的日志 LSN。
- Master Record:记录最近一次成功检查点的起始位置 LSN。
2. 关键结论 / 方法:ARIES 三阶段
- 分析阶段 (Analysis Phase):
- 从 Master Record 指向的检查点开始,正向扫描日志。
- 重建崩溃时刻的 ATT (哪些事务没跑完) 和 DPT (哪些页可能没刷盘)。
- 重做阶段 (Redo Phase),:
- “重演历史” (Repeat History)。
- 从 DPT 中最小的
RecLSN开始扫描。 - 重做所有操作(包括已提交和未提交的),除非
PageLSN>= Log 记录的 LSN(说明已落盘)。
- 撤销阶段 (Undo Phase),:
- 逆向扫描日志,撤销 ATT 中所有未提交事务(Loser Transactions)的修改。
- CLR (Compensation Log Record):在撤销操作时,写入一条“补偿日志”。关键点:CLR 永远不需要被 Undo。这保证了如果在恢复过程中再次崩溃,系统不会陷入死循环。
3. 易混点提示
- Redo 为什么重做未提交事务? ARIES 的哲学是先将数据库恢复到崩溃那一刻的完全状态(Repeat History),然后再统一回滚未提交事务。这样简化了逻辑。
- 什么时候写 CLR? 只有在 Undo 阶段(或运行时 Abort)回滚操作时才写。
- LSN 的比较:只有当
PageLSN <= FlushedLSN时,我们才能确信该页对应的日志已经落盘,从而安全地将该页写盘(WAL 规则)。
4. 复习小结
ARIES 通过分析、重做、撤销三个阶段,结合 LSN 机制,完美实现了 Steal + No-Force 策略下的数据恢复。CLR 的引入解决了恢复期间再次崩溃(Nested Crash)的问题。
综合复习思考题 (Self-Check)
- 如果系统使用 WAL,当数据库崩溃重启时,为什么 Redo 阶段要从 DPT 中最小的 RecLSN 开始,而不是从检查点开始?
- 提示:因为检查点时刻已有的脏页可能在其之前很久就被修改过,RecLSN 记录了它最早变脏的时刻。
- 在 ARIES 的 Undo 阶段,如果遇到了 CLR 记录,应该怎么处理?
- 提示:直接跳过,因为 CLR 意味着该操作已经被撤销过了,且 CLR 本身不可撤销。