6 数据库恢复
6.1 数据库故障
- 事务故障:资源冲突导致死锁
- 系统故障(软件)
- 磁盘故障(硬件):或者其他非易事存储介质
- 自然灾害。。。
6.2 缓冲池策略
数据库系统产生错误,在RAM中的数据没写到磁盘就消失了
如何恢复?
- 撤销未完成(ROLLBACK)修改
- 重做已完成(COMMIT)修改
未提交事务
- 允许修改磁盘:STEAL,缓冲池效率高(占用完就消除了)
- 不允许:No-STEAL,效率低,并发低(一直占用)
以提交事务
- 强制修改:FORCE,I/O效率低(占用I/O接口,带宽满了就得等)
- 不强制修改:No-FORCE,效率高
上面有4种组合
考虑下面的故障,四种组合的恢复策略分别是

No-STEAL AND FORCE
执行效率低,但是恢复最友好,T1T2都不需要任何操作
NO-STEAL AND No-FORCE
需要重做T1
STEAL AND FORCE
需要撤销T2
STEAL AND No-FORCE
执行效率高但是恢复难度高,T1重做,T2撤销,最麻烦
6.3 数据库日志
日志记录的序列,顺序写入磁盘,不会修改;只能写不能改
6.3.1 Undo回滚日志
格式:<T,X.v_old>,分别是事务标识符,数据项,数据项修改前的值
产生时机:T修改X的值(W(X))时产生
作用:用于回滚
6.3.2 Redo重做日志
格式:<T,X.v_new>,分别是事务标识符,数据项,数据项修改后的值
产生时机:T修改X的值(W(X))时产生
作用:用于重做
6.3.3 预写日志WAL
日志必须在磁盘中才有效,且必须比数据先写入磁盘(Write Ahead Logging)
日志写回磁盘的顺序和生成的顺序一致,且只有<T,commit>写入磁盘后,事务T才算结束
因此事务执行前必先写日志,然后才能做缓冲池策略,恢复时就看日志
6.4 故障恢复机制
6.4.1 事务分类
分为三类:
- 已完成,存在<T,start>和<T,commit>,没写入磁盘需要redo
- 不完整,只有<T,start>,已经写入磁盘需要undo
- 已终止,存在<T,start>和<T,rollback>,不需要恢复
6.4.2 No-STEAL AND FORCE
使用影子拷贝法,将原始数据拷贝到另一个数据库(再开空间),修改完后,事务提交完才提交到原始数据库
空间开销大,不实用
6.4.3 STEAL AND FORCE
需要WAL和Undo,不需要Redo,使用Undo日志,恢复流程如下
- 找到未提交的事务
- 回滚这些事务
- 写入<T,rollback>
恢复时,从后向前看日志一次,每条记录有以下动作
- <T,commit>,将T标记为已提交,不操作
- <T,rollback>,将T标记,不操作
- <T,X,v_old>,如果T没有任何标记,那么将X恢复为v_old
- <T,start>,如果T没有标记(不完整),在最下方下入<T,rollback>
6.4.4 No-STEAL AND No-FORCE
需要WAL和Redo,不需要Undo,使用Redo日志,恢复流程如下
- 找到提交的事务
- 重做这些事务
- 写入<T,commit>
恢复操作,整个日志顺序扫描两遍
- 第一次,记录所有commit和rollback并标记
- 第二次,<T,X,v_new>,根据T的标记,commit就将X写为v_new,rollback就不管
- 对于<T,start>,没有标记的,那么就写一个rollback
6.4.5 STEAL AND No-FORCE
需要WAL,Redo,Undo,使用混合日志,恢复流程就是上面两个的混合
记录流程
- 开始事务,<T,start>
- 修改事务,<T,X,v_old,v_new>
- 提交事务,<T,commit>
- 终止事务,<T,rollback>
恢复流程
第一次顺序扫描,记录所有的commit和rollback
有start而没有commit/rollback,则需要撤销;有start且有commit,需要重做
第二次顺序扫描,重做commit的T,也叫重放历史
第三次逆序扫描,**撤销没有标记(不完整)**的T
6.5 检查点
只会读取检查点之后的日志
6.5.1 全量检查点
设置流程
- 停止所有事务
- 将RAM没写的数据写到磁盘
- 记录检查点
- 恢复事务
6.5.2 涉及检查点的恢复
- 检查点之前完成(commit/rollback)的事务不用管
- 检查点之后的commit需要redo
- 所有未完成的事务需要undo