数据库的日志系统与恢复机制
字数 1467 2025-11-03 12:22:58
数据库的日志系统与恢复机制
描述
数据库的日志系统与恢复机制是确保数据一致性和持久性的核心组件。当数据库发生故障(如断电、系统崩溃)时,日志系统通过记录操作历史,使数据库能够恢复到一致状态。核心问题包括:日志记录什么内容?如何保证日志优先写入?故障后如何利用日志恢复?
1. 日志的基本内容与类型
日志是顺序追加的文件,记录数据库的所有变更操作。每条日志包含:
- 事务标识(Transaction ID):唯一标识一个事务。
- 操作类型:如“插入”“删除”“更新”。
- 数据项标识:被修改的数据位置(如页面ID+偏移量)。
- 旧值(Before Image):修改前的数据值(用于回滚)。
- 新值(After Image):修改后的数据值(用于重做)。
日志分为两类:
- 重做日志(REDO Log):记录事务提交后的新值,用于重现已提交的修改。
- 撤销日志(UNDO Log):记录事务修改前的旧值,用于回滚未提交的事务。
2. 日志的写入规则:预写式日志(WAL)
为保证持久性,数据库采用预写式日志(Write-Ahead Logging, WAL) 规则:
任何数据页的修改前,必须先将其对应的日志记录持久化到磁盘。
为什么需要WAL?
- 若先修改数据页,但日志未写入时发生崩溃,将无法恢复修改。
- WAL确保日志始终包含完整的操作历史,即使数据页丢失也可通过日志重建。
实现方式:
- 事务修改数据时,先在内存的日志缓冲区生成日志记录。
- 将日志缓冲区强制刷盘(
fsync),确保日志持久化。 - 再将数据页的修改写入内存的数据缓冲区。
- 数据缓冲区由后台进程异步刷盘。
3. 恢复机制的核心流程
故障恢复分为三个阶段:
阶段一:分析阶段(Analysis Phase)
- 扫描日志,确定崩溃时哪些事务未提交(需要回滚),哪些已提交但数据可能未刷盘(需要重做)。
- 建立两个集合:
- 重做集合(Redo Set):所有已提交或未提交但日志完整的事务。
- 撤销集合(Undo Set):所有未提交的事务。
阶段二:重做阶段(Redo Phase)
- 从最近一次检查点(Checkpoint)开始,重放所有重做集合中的操作:
- 对每条日志,若数据页的当前版本比日志旧(通过LSN对比),则重新应用新值。
- 目的:确保已提交事务的修改全部持久化,即使数据页未刷盘。
阶段三:撤销阶段(Undo Phase)
- 反向扫描日志,对撤销集合中的事务执行回滚:
- 用日志中的旧值覆盖数据页,并写入一条“补偿日志”记录回滚操作。
- 目的:消除未提交事务的部分修改,保证原子性。
4. 检查点(Checkpoint)优化
若每次恢复都扫描全部日志,效率极低。检查点机制定期执行:
- 暂停新事务,等待所有当前事务完成。
- 将脏数据页(修改未刷盘)强制刷盘。
- 在日志中写入一条检查点记录,记录当前活跃事务列表。
恢复时:只需从最近检查点开始扫描日志,减少日志处理量。
5. 实际应用中的日志类型
- 物理日志:记录数据页的具体字节变化(如“页面P偏移100处写入值X”),恢复效率高。
- 逻辑日志:记录SQL操作(如“UPDATE T SET col=1 WHERE id=5”),节省空间但恢复可能复杂。
现代数据库(如MySQL InnoDB)常结合两者:物理日志用于重做,逻辑日志用于回滚。
总结
日志系统通过WAL规则和恢复流程,在故障后能:
- 重做已提交事务(保证持久性)。
- 撤销未提交事务(保证原子性)。
- 检查点机制平衡了恢复性能与运行时开销。