https://www.cnblogs.com/takumicx/p/9998844.html#1-%E8%AE%A4%E8%AF%86%E4%BA%8B%E5%8A%A1
3.1 为什么需要故障恢复技术
数据库运行过程中可能会出现故障,这些故障包括事务故障和系统故障两大类
- 事务故障:比如非法输入,系统出现死锁,导致事务无法继续执行。
- 系统故障:比如由于软件漏洞或硬件错误导致系统崩溃或中止。
这些故障可能会对事务和数据库状态造成破坏,因而必须提供一种技术来对各种故障进行恢复,保证数据库一致性,事务的原子性以及持久性。数据库通常以日志的方式记录数据库的操作从而在故障时进行恢复,因而可以称之为日志恢复技术。
3.2 事务的执行过程以及可能产生的问题

事务的执行过程可以简化如下:
系统会为每个事务开辟一个私有工作区
事务读操作将从磁盘中拷贝数据项到工作区中,在执行写操作前所有的更新都作用于工作区中的拷贝.
事务的写操作将把数据输出到内存的缓冲区中,等到合适的时间再由缓冲区管理器将数据写入到磁盘。
由于数据库存在立即修改和延迟修改,所以在事务执行过程中可能存在以下情况:
- 在事务提交前出现故障,但是事务对数据库的部分修改已经写入磁盘数据库中。这导致了事务的原子性被破坏。
- 在系统崩溃前事务已经提交,但数据还在内存缓冲区中,没有写入磁盘。系统恢复时将丢失此次已提交的修改。这是对事务持久性的破坏。
3.3 日志的种类和格式
<T,X,V1,V2>:描述一次数据库写操作,T是执行写操作的事务的唯一标识,X是要写的数据项,V1是数据项的旧值,V2是数据项的新值。<T,X,V1>:对数据库写操作的撤销操作,将事务T的X数据项恢复为旧值V1。在事务恢复阶段插入。<T start>: 事务T开始<T commit>: 事务T提交<T abort>: 事务T中止
关于日志,有以下两条规则
- 1.系统在对数据库进行修改前会在日志文件末尾追加相应的日志记录。
- 2.当一个事务的commit日志记录写入到磁盘成功后,称这个事务已提交,但事务所做的修改可能并未写入磁盘
3.4 日志恢复的核心思想
撤销事务undo:将事务更新的所有数据项恢复为日志中的旧值,事务撤销完毕时将插入一条
<T abort>记录。重做事务redo:将事务更新的所有数据项恢复为日志中的新值。
事务正常回滚/因事务故障中止将进行redo
系统从崩溃中恢复时将先进行redo再进行undo。
以下事务将进行undo:日志中只包括<T start>记录,但既不包括<T commit>记录也不包括<T abort>记录.
以下事务将进行redo:日志中包括<T start>记录,也包括<T commit>记录或<T abort>记录。
假设系统从崩溃中恢复时日志记录如下
<T0 start>
<T0,A,1000,950>
<T0,B,2000,2050>
<T0 commit>
<T1 start>
<T1,C,700,600>
由于T0既有start记录又有commit记录,将会对事务T0进行重做,执行相应的redo操作。
由于T1只有start记录,将会对T1进行撤销,执行相应的undo操作,撤销完毕将写入一条abort记录。
3.5 事务故障中止/正常回滚的恢复流程
从后往前扫描日志,对于事务T的每个形如
<T,X,V1,V2>的记录,将旧值V1写入数据项X中。往日志中写一个特殊的只读记录
<T,X,V1>,表示将数据项恢复成旧值V1,
这是一个只读的补偿记录,不需要根据它进行undo。一旦发现了
<T start>日志记录,就停止继续扫描,并往日志中写一个<T abort>日志记录。
3.6 系统崩溃时的恢复过程(带检查点)
检查点是形如<checkpoint L>的特殊的日志记录,L是写入检查点记录时还未提交的事务的集合,系统保证在检查点之前已经提交的事务对数据库的修改已经写入磁盘,不需要进行redo。检查点可以加快恢复的过程。
系统奔溃时的恢复过程分为两个阶段:重做阶段和撤销阶段。
重做阶段:
系统从最后一个检查点开始正向的扫描日志,将要重做的事务的列表undo-list设置为检查点日志记录中的L列表。
发现
<T,X,V1,V2>的更新记录或<T,X,V>的补偿撤销记录,就重做该操作。发现
<T start>记录,就把T加入到undo-list中。发现
<T abort>或<T commit>记录,就把T从undo-list中去除。
撤销阶段:
系统从尾部开始反向扫描日志
发现属于undo-list中的事务的日志记录,就执行undo操作
发现undo-list中事务的T的
<T start>记录,就写入一条<T abort>记录,
并把T从undo-list中去除。
4.undo-list为空,则撤销阶段结束
总结:先将日志记录中所有事务的更新按顺序重做一遍,在针对需要撤销的事务按相反的顺序执行其更新操作的撤销操作。
3.6.1 一个系统崩溃恢复的例子
恢复前的日志如下,写入最后一条日志记录后系统崩溃
<T0 start>
<T0,B,2000,2050>
<T2 commit>
<T1 start>
<checkpoint {T0,T1}> //之前T2已经commit,故不用重做
<T1,C,700,600>
<T1 commit>
<T2 start>
<T2,A,500,400>
<T0,B,2000>
<T0 abort> //T0回滚完成,插入该记录后系统崩溃





