一般都是通过WAL实现的
也就是在更改写入磁盘之前
确保描述更改的日志先被持久化
下面具体就IBM发明的ARIES技术来展开描述一些
共有3个阶段: 分析阶段、redo阶段和undo阶段
分析阶段
该阶段的目的是恢复系统崩溃之前的事务表和脏页面表。
过程是:
- 读取
主记录,从而获取最后一次checkpoint动作的begin checkpoint记录的LSN,并在WAL中定位到该记录。
- 用前述
begin checkpoint记录之后的end checkpoint记录中的数据初始化脏页面表和事务表。
- 对
checkpoint动作开始之后的每条记录r:
- 如果
r是某个事务T的end记录,则将T从事务表中移除,因为它已经完成。
- 如果
r是事务T的非end记录,则确保T存在于事务表中。并且用r更新事务表:更新T的lastLSN域,并且如果它已经开始commit,则记录下该事务的状态。
- 如果
r是指向页面P的update或者CLR记录,确保P存在于脏页面表中。
- 到这里,我们已经获得了最新的事务表和脏页面表;它们将用于下一阶段。
注意这个阶段构造的脏页面表可能是非常保守的:
最后一次checkpoint动作之后WAL记录对页面的修改可能在系统崩溃前已经回填,
即它们可能实际并不脏。
但是由于我们能检测并避免已经执行的update,所以这不是个问题。
redo阶段
该阶段利用事务表和脏页面表,以及WAL,redo系统崩溃之前的所有更改。
该阶段过程是:
- 从脏页面表中找出最小的
recLSN(代表可能还未回填的最老的WAL记录)
- 对于每个
LSN大于或等于前述recLSN的记录r:
- 如果
r不是update或者CLR记录,忽略之。
- 如果
r的目标页面不在脏页面表中,忽略之。
- 如果
r的目标页面在脏页面表中,但该页面的recLSN大于r的LSN,忽略之。
- 如果
r的目标页面的page_LSN大于或等于r的LSN,忽略之(该步需要获取页面的实际信息)。
- 否则,
r应该被执行。从而,r的目标页面得到更改,且其page_LSN也被置为r的LSN。
注意这个阶段也会redo这2种事务的更改:
- 系统崩溃前已经放弃的事务
- 事务在系统崩溃前仍处于
in-progress状态(是被系统放弃而非主动放弃)。
前者的更改会被WAL中的CLR记录undo;后者的更改会在接下来的undo阶段undo。
undo阶段
到达这个阶段,所有相关的记录已经被redo;系统的状态应该与崩溃之前完全一样。
最后要做的一件事是放弃所有系统崩溃前仍然active的事务,并且undo他们的更改。
过程如下:
- 此时事务表中的事务都是需要放弃的:称之为失败事务。每个事务的
lastLSN域标记着该事务所做的最后一次更改的WAL记录。我们希望undo失败事务的更改,以与更改相反的顺序一路回退。故与其他阶段不同,该阶段从后往前读取WAL.
- 构建一张
ToUndo表,初始值为所有失败事务的lastLSN。直到该表为空,重复以下步骤:
- 从
ToUndo表移除最大的LSN项(即最后写入的WAL记录)。并获取对应的记录r。
- 如果
r为update记录,则其更改需要undo。向WAL写入一条CLR之后便undo其更改。并将r的prevLSN添加到ToUndo表。
- 如果
r是CLR记录,我们知道其所描述的undo动作已在redo阶段被redo。所以这里不需要再次undo。仅需要查看它的undoNextLSN域,如果是NULL,那就说明我们已经完成了该事务的undo,所以向WAL写入一条end记录之后便可将r扔掉。否则,将undoNextLSN添加到ToUndo表。
一旦ToUndo表为空,恢复工作便告完成。
如果该阶段再次发生系统崩溃:
该阶段与前2个阶段不同,它会修改WAL,所以需要足够的健壮使得该阶段即使系统再次崩溃也不受影响。
- 我们知道系统的状态由
WAL和磁盘数据共同构成。
- 该阶段通过
undo失败事务的更改来修改磁盘数据。但是,在更改被undo之前,都会先写入描述该操作的CLR记录。如果系统在该阶段完成前再次崩溃,该CLR记录会在redo阶段再次被redo。从而保证了即使发生系统崩溃数据最终还是能够被修改。
- 该阶段向
WAL写入CLR。但是,这不能避免系统崩溃。CLR所undo的记录在WAL中位于CLR之前;因此,在redo阶段看到CLR时,其undo的记录已经被redo,从而CLR也能被redo。当在undo阶段遇到该CLR时,它的修改已经被redo —— 我们要做的只是把CLR的undoNextLSN添加到ToUndo表。