ARIES,一种数据库恢复算法

故障分类

  • 事务故障:因为逻辑错误(比如数据越界)或者系统错误(比如检测到死锁)导致事务执行失败。这种情况会abort掉事务,不会丢失易失性存储的内容。

  • 系统故障:数据库软件、操作系统自身的漏洞,或者硬件故障,导致易失性存储内容丢失,但是非易失性存储(硬盘)内容不丢失。

  • 磁盘故障:磁盘损坏,可以通过数据备份恢复,暂不考虑这种。

数据访问

约定有以下操作:

  • input(B),将磁盘上的物理块传送至主存
  • output(B),将主存上的物理块传送至主盘

在概念上,每个事务T有一个自己的工作区。(比如我们在写执行算子时,可以临时建一个tuple对象来保存数据)。

  • read(X)
    • 若X所在块不在buffer pool中,首先执行input(B)
    • 将缓冲块中的X的值赋予临时工作区。
  • write(X)
    • 若X所在块B不在主存中,首先input(B)
    • 将X修改后的值赋予B中对应的数据项。

日志系统

接下来介绍日志(log),日志是**日志记录(log record)**的序列。

  1. 更新日志记录(update log record)是描述一次数据库写操作的记录。一开始我因为更新这个词困惑了很久。❌之前错误地认为,update包含了DML的插入、删除、修改操作。实际上,因为insert和delete可能导致slot的移动,(比如一个page上有三条记录,插入一条记录后出现了新的一页),这种情况不是单纯的通过新旧值就能解决的,之后的逻辑操作会解决这个问题。

一个update log record包含以下内容:

  • 事务标识
  • 数据项标识
  • 旧值
  • 新值

通过日志,我们可以进行以下操作:

  • redo( T i T_i Ti): 重做事务 T i T_i Ti,将其所有数据项的值设为新值。通常数据库在阅读日志,进行redo操作时,都是直接重做,而不是将事务分开分别重做。(因为事务在日志中的记录可能不是连续的,多个事务同用一个log manager。比如记录为t1,t2,t3,t1,t2,t3。数据库直接按照这个记录来重做,而不是先去寻找t1,再去寻找t2这种耗时又没实际效果的模式)
  • undo( T i T_i Ti),将 T i T_i Ti的所有值设为旧值。
    • 注意,在undo操作时,这个行为本身也要留下被称之为read-only的记录,但是这个记录是永远不会被undo的。换句话说,undo这个操作会被记录,记录也是撤销过程的一部分。
    • 之前不理解为什么undo操作也要留下记录,觉得如果事务要中止掉的话,直接通过redo记录来undo不行吗?实际上,该记录可以减少算法的复杂度。比如一个事务abort掉,那么从就到新的记录直接一路redo就行了(undo的记录也要redo),到最后自然事务abort掉了。而不需要再从后往前通过redo记录来undo。
  1. 描述事务操作的记录
    • < T i s t a r t > <Tistart> 标识着一个事务开始
    • < T i c o m m i t > <Ticommit> commit操作标识事务提交。写入该记录是一个原子操作。就是说,我们在日志从头到尾读log record,当遇到一条commit记录,就标识着该事务已经提交,否则中止该事务(没有找到commit标识)。
    • < T i a b o r t > <Tiabort> 事务中止标识。

为了保证数据库的持久性,我们下意识地反应:日志记录必须可以马上加到稳定存储器的日志文件后。对又不对,因为磁盘读写通常是以块为单位的,而单条record通常远小于这个值。我们得多个多个地写入log record。接下来我们将讨论如何做到。

  1. Checkpoint

假如我们插入了很多记录,比如一百万条,而这些操作早就落盘了。那么我们实际上没必要完整读完这些记录的。为此,我们引入了一种新的记录:,其中,L是在记录检查点时,还未提交的事务。

目前,我们简单的制作检查点的过程如下

  • 将日志落盘
  • 将所有缓冲输出到磁盘
  • 将日志记录输出到稳定存储器。

在恢复阶段,首先数据库从后往前找到第一个检查点(最新的检查点)。对于L(制作检查点时活跃的事务列表),以及在checkpoint之后新出现的事务,作为一个事务集合T

对于T中的每个事务

  • 如果该事务没有commit或abort,需要undo该事务。
  • 否则redo该事务。

举个例子。假如我们的数据库将要处理了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事务指令。

  1. 从后往前扫描日志,对于每个该事务的记录,将旧值V1写入Xj中。
  2. 增加一个特殊的只读日志记录,这种记录也被称为CLR,补偿日志记录(compensation log record)
  3. 当发现了start的记录,停止扫描,并增加abort记录

系统崩溃之后的恢复

当数据库系统启动时,会进行恢复操作

  1. 重做阶段。redo
    • 系统首先找到最后一个检查点,从前往后扫描并重做所有遇到的事务。
    • 这个重做包括之前提到的CLR,也就是说,都会重做Xj的值为V2。
    • 这样,遇到了Abort就不再需要从后再往前扫描一遍了。
    • 在redo阶段,需要维护一个活跃事务列表,该列表初值为Check point中的L,遇到commit或abort就会将L中的该事务去掉,遇到start就会在L中增加事务。
    • 最后,会得到一个事务列表。undo-list
  2. 撤销阶段。undo
    • 事务从后往前回滚所有undo-list中的事务。
    • 请注意,CLR是不会被undo的。
    • 具体操作和系统正常运行时abort掉的流程相似,当遇到start记录,从undo-list中删掉该事务,当list为空,undo阶段结束。

思考一个细节,当事务在abort过程中,系统崩溃掉了。那么如何利用留下的记录恢复呢?

缓冲管理

先写日志 WAL的规则

  • 在 commit记录输出到稳定存储器后,事务进入提交状态
  • 在commit记录输出前,所有与该事务有关的记录已经output到磁盘
  • 当缓存中的数据块 输出到稳定存储器前, 有关的记录必须已经输出到了稳定存储器。

采用No-Force(允许事务commit没有输出到稳定存储器的更改),Steal(允许没有commit的块输出到磁盘)策略。当一个块输出时,采用以下序列:

  • 获取到该块上的排他锁
  • 将日志记录输出,确保与该块的所有日志记录已经输出
  • 将块B输出到磁盘
  • 释放锁

逻辑undo

插入或删除是逻辑操作的一种例子,这种操作不能简单地通过将数据项设为旧值来撤销。(比如之前删除了一个数据项,你在哪里设置旧值呢,这个数据项已经不存在了。)

新概念:幂等。当一个操作在一行上执行多次,结果都是相等的,则该操作是幂等的。物理日志记录是幂等的,逻辑操作不是幂等的。

逻辑undo引入了新的日志类型:

,标识逻辑操作的开始

标识逻辑操作结束,根据undo info来进行撤销操作。

比如物理操作是将x的400变为500,undo info是(x-100),表示撤销时应该将值减去100,而不是设为400(因为可能受到其他事务影响)

ARIES

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是一种具体的数据库恢复算法。

  1. 使用LSN来标识log序号,并且在page的head部分存储lsn序号信息。

ARIES,一种数据库恢复算法_第1张图片

  1. 支持物理逻辑redo操作。
  2. 使用脏页表 DPT(dirty page table)来记录内存中已经更新,磁盘上未更新的页。
  3. 使用模糊检查点机制。

简要介绍ARIES的恢复算法

  1. 分析阶段

首先找到最后的checkpoint,获取脏页表。通过脏页表中最小的reclsn确定从哪个lsn开始重做。如果没有脏页,就将redolsn设置为checkpoint的lsn。也就是说,redo lsn之前的所有record都是已经确保写入了磁盘的。

在分析阶段,还需要维护undo-list,和undo-list中事务的lastLSN。

一旦分析阶段发现在页上更新的日志记录,还将更新脏页表。新的脏页的rec lsn为该log的lsn

  1. 重做阶段

通过从前往后扫描log。如果该log的页不在脏页表中,或者更新日志记录的LSN小于脏页表中该页的rec lsn(该log已经落盘),就跳过该次记录

否则就调出该页,如果该页的page lsn小于该日志的lsn,重做日志。

  1. 撤销阶段

撤销阶段会对日志进行反向扫描,并对undo list中的所有事务进行撤销。

如果遇到一个更新日志记录,就用其进行物理undo,并产生一条CLR,将该CLR的UndoNextLSN设置为该日志的prev LSN。

如果遇到一条CLR,说明在重做阶段已经进行了回滚操作,那么直接跳到UndoNextLSN就行。

你可能感兴趣的:(数据库,数据库)