事务故障:因为逻辑错误(比如数据越界)或者系统错误(比如检测到死锁)导致事务执行失败。这种情况会abort掉事务,不会丢失易失性存储的内容。
系统故障:数据库软件、操作系统自身的漏洞,或者硬件故障,导致易失性存储内容丢失,但是非易失性存储(硬盘)内容不丢失。
磁盘故障:磁盘损坏,可以通过数据备份恢复,暂不考虑这种。
约定有以下操作:
在概念上,每个事务T有一个自己的工作区。(比如我们在写执行算子时,可以临时建一个tuple对象来保存数据)。
接下来介绍日志(log),日志是**日志记录(log record)**的序列。
一个update log record包含以下内容:
通过日志,我们可以进行以下操作:
为了保证数据库的持久性,我们下意识地反应:日志记录必须可以马上加到稳定存储器的日志文件后。对又不对,因为磁盘读写通常是以块为单位的,而单条record通常远小于这个值。我们得多个多个地写入log record。接下来我们将讨论如何做到。
假如我们插入了很多记录,比如一百万条,而这些操作早就落盘了。那么我们实际上没必要完整读完这些记录的。为此,我们引入了一种新的记录:
,其中,L是在记录检查点时,还未提交的事务。
目前,我们简单的制作检查点的过程如下
输出到稳定存储器。在恢复阶段,首先数据库从后往前找到第一个检查点(最新的检查点)。对于L(制作检查点时活跃的事务列表),以及在checkpoint之后新出现的事务,作为一个事务集合T
对于T中的每个事务
举个例子。假如我们的数据库将要处理了1-10编号的事务。然后制作检查点,制作检查点时,1-4已经commit了。6也commit了。5和7已经start,还未abort或commit掉。8,9,10在checkpoint之后才start。
那么制作检查点时,会先将前面的log都写入磁盘。然后将buffer pool中的所有脏页面刷入磁盘。最后在log后加上checkpoint (5,7)。
因为1234已经commit了,那么系统可以清理掉
之前的所有日志。
目前所介绍的checkpoint需要暂停所有事务,并刷新磁盘。之后在ARIES算法中会介绍fuzzy checkpoint(模糊检查点),解决阻塞问题。
比如当手动发出abort事务指令。
,将旧值V1写入Xj中。
,这种记录也被称为CLR,补偿日志记录(compensation log record)当数据库系统启动时,会进行恢复操作
和
都会重做Xj的值为V2。思考一个细节,当事务在abort过程中,系统崩溃掉了。那么如何利用留下的记录恢复呢?
先写日志 WAL的规则
采用No-Force(允许事务commit没有输出到稳定存储器的更改),Steal(允许没有commit的块输出到磁盘)策略。当一个块输出时,采用以下序列:
插入或删除是逻辑操作的一种例子,这种操作不能简单地通过将数据项设为旧值来撤销。(比如之前删除了一个数据项,你在哪里设置旧值呢,这个数据项已经不存在了。)
新概念:幂等。当一个操作在一行上执行多次,结果都是相等的,则该操作是幂等的。物理日志记录是幂等的,逻辑操作不是幂等的。
逻辑undo引入了新的日志类型:
,标识逻辑操作的开始
标识逻辑操作结束,根据undo info来进行撤销操作。
比如物理操作是将x的400变为500,undo info是(x-100),表示撤销时应该将值减去100,而不是设为400(因为可能受到其他事务影响)
ARIES: a transaction recovery method supporting fine-granularity locking and partial rollbacks using write-ahead logging: ACM Transactions on Database Systems: Vol 17, No 1
ARIES是一种具体的数据库恢复算法。
简要介绍ARIES的恢复算法
首先找到最后的checkpoint,获取脏页表。通过脏页表中最小的reclsn确定从哪个lsn开始重做。如果没有脏页,就将redolsn设置为checkpoint的lsn。也就是说,redo lsn之前的所有record都是已经确保写入了磁盘的。
在分析阶段,还需要维护undo-list,和undo-list中事务的lastLSN。
一旦分析阶段发现在页上更新的日志记录,还将更新脏页表。新的脏页的rec lsn为该log的lsn
通过从前往后扫描log。如果该log的页不在脏页表中,或者更新日志记录的LSN小于脏页表中该页的rec lsn(该log已经落盘),就跳过该次记录
否则就调出该页,如果该页的page lsn小于该日志的lsn,重做日志。
撤销阶段会对日志进行反向扫描,并对undo list中的所有事务进行撤销。
如果遇到一个更新日志记录,就用其进行物理undo,并产生一条CLR,将该CLR的UndoNextLSN设置为该日志的prev LSN。
如果遇到一条CLR,说明在重做阶段已经进行了回滚操作,那么直接跳到UndoNextLSN就行。