数据库恢复技术
数据库恢复技术
复习定位
数据库系统中可能发生各种故障(事务故障/系统崩溃/介质损坏)——必须保证故障之后数据恢复到一致性状态。恢复的基础是日志文件——日志记录每个事务的更新操作。从日志恢复的过程——REDO(重做所有已提交事务的已写入日志但可能尚未写入数据文件的更新)和UNDO(撤销所有未提交事务的修改)。
故障类型
事务故障——单个事务在执行过程中发生错误(死锁被选中回滚/违反完整性约束/应用程序异常退出)。事务故障的恢复——系统自动地撤销该事务所有已做的修改——将数据恢复到事务开始的初始状态。
系统故障——系统崩溃(操作系统错误/CPU故障/电源中断)。系统重新启动时——恢复程序检查日志:
- 对已提交事务——重新执行REID操作(将其更新写入数据文件——保证持久性)
- 对未完成事务(无COMMIT记录)——执行UNDO操作(撤销所有修改——保证原子性)
介质故障——磁盘物理损坏(磁头碰撞/电源击穿/存储介质老化)。需要DBA(Database Administrator——数据库管理员)恢复整个数据库——先利用最近的全量备份恢复整个数据库——然后再利用该备份之后的日志归档中的所有操作重做——使数据库恢复到故障前那一刻的状态。
基于日志的恢复
日志记录的格式通常为——<T, X, old_value, new_value>(事务T修改了数据项X——旧值为old——新值为new)。日志必须在数据页修改之前被写入稳定存储器(WAL——先写日志原则)——这样即使用户程序在写入数据过程中崩溃——日志中仍有完整的REDO/UNDO信息。
REDO——已提交事务的更新(日志中有COMMIT记录)但尚未写回数据文件——恢复时正向扫描日志——将new_value写到X。UNDO——未提交事务——反向扫描日志——将old_value写回X。
检查点
检查点是恢复可以开始的参考位置——它保证在检查点之前、所有已提交事务所做的修改都已写入数据文件——使恢复程序只需要重做检查点之后的事务——不需要扫描全部日志。
尖锐检查点——在检查点时刻停止接受新事务——将所有脏页刷回磁盘——记录检查点——恢复时只需从检查点开始扫描。
模糊检查点——不停止事务——在检查点期间记录活跃事务列表。恢复时扫描先确定redo和undo的范围——不用完全等待所有脏页刷盘。
恢复策略
- 事务故障——只做UNDO(撤销未能完成的更新)
- 系统故障——REDO所有已提交的但未写入数据文件的更新——UNDO所有未提交的
- 介质故障——恢复最近的完整备份+REDO所有在此备份后的归档日志
复习检查
WAL(Write-Ahead Log)原则——为什么在修改任何数据页之前——必须先确保日志已安全地写入稳定存储器——保证在系统崩溃时恢复过程有足够的信息重构(重做或撤销)所有修改?
REDO和UNDO使用的数据——分别使用日志记录中的new_value和old_value——系统故障恢复时——如果事务T的COMMIT记录已写入日志但数据未写入磁盘——恢复系统会对T执行REDO还是UNDO?
检查点为什么可以缩短恢复时间——扫描日志时从检查点开始而非从头开始——减少了日志中需要处理的事务数——检查点之前的所有已提交事务的修改均已写入磁盘。
介质故障恢复为什么要使用备份——备份是全量复制——加上后续的日志归档——可以回放到任意时间点(时间点恢复PITR)——在失误性操作(如DROP TABLE)发生后恢复到该操作前的瞬间。
事务回滚时——如果系统在UNDO过程中再次崩溃——重启后是否需要第二次UNDO——日志记录具有幂等性(重复UNDO同一记录不会造成影响)——因为UNDO将数据恢复到old_value——再UNDO一次仍然是old_value。
三种故障类型的详细特征对比
| 故障类型 | 故障范围 | 恢复策略 | 恢复介质 | 是否需要DBA介入 |
|---|---|---|---|---|
| 事务故障 | 单个事务 | UNDO回滚 | 内存中的undo日志 | 不需要(自动) |
| 系统故障 | 整个系统 | REDO+UNDO | 磁盘上的日志文件 | 不需要(自动) |
| 介质故障 | 磁盘物理损坏 | 备份+归档日志REDO | 备份文件+日志归档 | 需要DBA操作 |
事务故障是影响最小的——仅影响一个未完成的事务——数据库重启后自动回滚。系统故障影响所有在内存中未刷盘的数据——但磁盘数据文件本身没有损坏——恢复时可以依赖日志完成REDO和UNDO。介质故障是最严重的——磁盘设备本身的物理损坏导致数据文件丢失——必须依赖之前的外部备份和日志归档才能恢复。
WAL原则的详细解释
WAL(Write-Ahead Logging)原则:在对数据页做任何修改之前——必须先将描述该修改的日志记录(包含REDO和UNDO所需的信息)写入持久化的日志文件中。这个原则保证了:
- 如果系统在数据页写入过程中崩溃——日志中已有完整的信息可以回滚(UNDO)未完成的事务。
- 如果系统在数据页写入之前崩溃——但日志中已有COMMIT记录——重启时可以通过REDO将修改重新应用到数据页上。
WAL背后的性能动机:日志写入是顺序I/O——追加写入到日志文件的末尾——写入速度远快于数据页的随机I/O——因此先将日志写到磁盘——后续可以在后台异步地将数据页写入磁盘——不会因为频繁的数据页随机写而影响前台事务的响应速度。
检查点的不同类型及实现
尖锐检查点(Sharp Checkpoint):
1. 暂停所有新事务开始
2. 等待当前所有活跃事务完成
3. 将所有脏页(缓冲池中修改过但未刷入磁盘的页)全部强制写入磁盘
4. 在日志中写入检查点记录
5. 恢复时——检查点之前的所有已提交事务的修改都已写入磁盘——无需重做
模糊检查点(Fuzzy Checkpoint):
1. 在日志中写入Checkpoint_Begin(记录当前活跃事务列表和脏页信息)
2. 不停止正在执行的事务——后台线程异步刷脏页
3. 当所有检查点记录中的脏页都已刷入磁盘——写入Checkpoint_End
4. 恢复时——从Checkpoint_Begin开始扫描——通过活跃事务列表确定REDO/UNDO的范围
5. 不需要等待所有脏页都刷盘——系统可以继续提供服务模糊检查点是现代数据库(PostgreSQL、MySQL InnoDB)的主流实现——因为它不影响正常的事务执行——而尖锐检查点需要停止新事务——在高并发环境下会造成明显的服务停顿。
介质故障恢复的完整步骤
1. DBA检测到磁盘故障(数据库无法启动——操作系统报告磁盘I/O错误)
2. DBA安装新磁盘——在修复硬件或从阵列中替换故障盘
3. 从最近的完整备份中恢复数据文件(全量备份——可能是数天前的)
4. 将全量备份之后的所有日志归档(连续的WAL归档)按顺序应用到恢复的数据上
5. 数据库恢复到故障前的状态
6. 启动数据库——验证数据完整性在实际的企业环境中——全量备份通常是每天一次——日志归档是持续进行的——因此在极端情况下可能丢失最多几秒钟的事务数据(若数据库的事务提交确认先返回给用户——但日志归档还来不及在到异地灾备系统介质之前就断了)——所以好的备份策略和RPO(恢复点目标)相关——将丢失窗口配置为用户所能忍受的时间范围。
复习检查(续)
WAL原则为什么必须先写日志再写数据——日志顺序写入(快)——数据页随机写入(慢)——先写日志保证崩溃后可恢复——延迟的数据页写入在后台异步执行可减少对前台事务的影响。
尖锐检查点与模糊检查点的主要区别——尖锐检查点停止所有新事务——将所有脏页刷盘——恢复时只需从检查点之后扫描——模糊检查点不停止事务——记录活跃事务列表并异步刷脏页——恢复时从检查点开始但需要处理活跃事务。
介质故障恢复为什么需要全量备份——全量备份提供数据文件的基线——日志归档提供基线之后的增量——两者结合可以恢复到故障前任意时间点。
事务故障的自动恢复过程——检测到事务异常(死锁回滚/约束违反等)→系统标记该事务为"终止"→自动撤销该事务所做的所有修改(通过UNDO日志)→释放该事务持有的所有锁→恢复正常操作。
系统故障恢复的REDO+UNDO顺序——先REDO(确保所有已提交事务的修改都已写入数据文件)——后UNDO(撤销所有未提交事务所做的修改)——因为REDO阶段不区分事务是否提交——重做所有操作——UNDO阶段只回滚未提交部分。
日志记录的格式和存储方式
数据库日志记录的格式一般包含:
LSN(Log Sequence Number): 日志序列号——唯一标识一条日志记录——单调递增
TransID: 所属事务的ID
PageID: 被修改的数据页的标识
Redo: 重做该操作所需的信息(通常是修改后的新值)
Undo: 撤销该操作所需的信息(通常是修改前的旧值)
PrevLSN: 同一事务上一条日志记录的LSN——用于快速定位事务的所有日志记录
Type: 日志记录类型(更新/提交/回滚/检查点等)日志文件通常存储在磁盘上的独立存储区域——与数据文件分开。PostgreSQL的WAL段文件每段16MB——连续写入直到段满——然后切换到下一个段文件。InnoDB的REDO日志是环形缓冲区(ib_logfile0/ib_logfile1)——预先分配固定大小——写入满了之后重新覆盖旧的已检查点处理过的部分。
日志归档(Log Archiving)
在生产数据库中——日志文件不仅可以用于崩溃恢复——还可以用于时间点恢复(PITR)和异地灾备。归档模式开启时——每个写满的日志段文件被复制到归档存储位置(通常是独立的网络存储或云存储)。在需要时——可以从全量备份开始——按顺序应用归档日志——将数据库恢复到任意时间点(甚至精确到秒)
PG的archive_command和MySQL的mysqlbinlog都是日志的基础工具——需要配合目录管理来应对GB甚至TB量级的日志归档文件。
ARIES恢复算法概述
ARIES(Algorithm for Recovery and Isolation Exploiting Semantics)是主流数据库(IBM DB2、MS SQL Server、Oracle)使用的恢复算法——其核心原则是:
- WAL(先写日志)——日志优先于数据写入
- REDO所有操作(包括未提交事务)——确保数据库恢复到崩溃时那一刻的物理状态——不论事务是否提交
- UNDO未提交事务——REDO完成后——只撤销那些没有COMMIT标记的事务
ARIES通过LSN(日志序列号)和Dirty Page Table(脏页表)确保——分析阶段从最近的检查点开始——确定redo的起始点和undo的事务列表——重做阶段将数据库恢复到崩溃时的物理状态——撤销阶段回滚所有未提交事务所做的修改。每次操作都记录prevLSN——使一个事务内的日志记录可以链式回溯查看更新——也方便在恢复时正向或反向穿越记录。
复习检查(续二)
日志记录的基本格式元素——LSN(单调递增日志序列号)、TransID、前映像(UNDO用)和后映像(REDO用)、PrevLSN(同一事务的前一条日志记录LSN——用于链式回溯)、类型标记。
REDO操作为什么对未提交的事务也执行——REDO阶段将数据库恢复到崩溃时的物理状态——包括未提交事务的修改——使数据库的物理状态与崩溃时刻一致——之后通过UNDO回滚未提交的修改。
ARIES算法的三个阶段——分析阶段(确定redo和undo的范围)——重做阶段(将数据库恢复到崩溃时刻的物理状态)——撤销阶段(回滚所有未提交事务的修改)。
PostgreSQL的WAL段文件大小和切换方式——每段16MB——写满后切换到下一个段文件——通过archive_command将写满的段文件复制到归档位置。
时间点恢复(PITR)的步骤——从全量备份恢复数据文件——手工设置恢复目标时间点——按顺序应用归档日志到目标时间点——数据库恢复到那个时刻的状态——停止恢复——启动数据库正常服务。
日志文件在数据库性能中的角色
日志文件的I/O模式几乎完全以顺序写入为主——所以数据库应该将日志文件放在独立的物理磁盘(最好是NVMe SSD)上——以避免日志写入和随机数据块读取/写入的主竞争。日志文件的顺序写入速度——现代NVMe SSD的顺序写入可以使日志持久化的延迟从数毫秒降至微秒级别——因此在设计数据库部署架构时——将重做日志文件(redo log)和数据文件分开至不同的NVMe驱动器是可行的架构——可以显著改善写密集型事务的延迟。
不同数据库的恢复机制差异
| 数据库 | 日志机制 | 检查点机制 | 归档方式 |
|---|---|---|---|
| PostgreSQL | WAL(预写式日志) | 模糊检查点 | archive_command→归档存储 |
| MySQL InnoDB | redo log + undo log | 模糊检查点 | 二进制日志(binlog) |
| Oracle | redo log + undo segment | 增量检查点 | 归档日志模式 |
| SQL Server | 事务日志(.ldf) | 自动检查点 | 完整/批量日志/简单三种恢复模式 |
不同数据库的恢复机制核心原理相似——都基于WAL原则和ARIES算法——但在具体实现细节(缓冲策略、归档方式、检查点算法)上有所差异。
数据库恢复技术对可用性的保障
数据库恢复技术提供了应对不同故障等级的恢复能力——事务故障(自动回滚——秒级恢复)、系统故障(自动REDO+UNDO——秒到分钟级恢复)、介质故障(备份+归档——分钟到小时级恢复取决于数据量)。理解恢复机制是DBA的关键能力——也是实现数据持久性和高可用性(HA)的基石。任何数据库运维实践中——都要围绕日志、备份和恢复展开——确保在故障发生后数据不丢失、应用可用性窗口满足服务等级协议的要求。