跳转至

数据库恢复

1.简介

如今,数据库恢复最流行的算法是由IBM提出的ARIES。其思想主要包括三个部分:

  • Write-Ahead Logging
  • Repeating History During Redo
  • Logging Changes During Undo

其中,undo操作同样也需要记录日记。这是为了保证在恢复的过程中若发生崩溃,则需要从恢复的崩溃中恢复。

2.Log Sequence Numbers

生成的日志都会被分配一个单调递增的数,用于唯一标识日志,称之为LSN。由于并发事务的存在,LSN不一定是连续的。

在系统、事务以及page中,会维护一些与LSN相关的metadata,如下表所示:

Name Where Definition
flushedLSN Memory 最后一个写入到磁盘的LSN序号。
pageLSN page 对于每个page来说,最后对该page作出修改的LSN。
recLSN page 使page标志dirty变为真的日志LSN。
LastLSN transaction 每个事务最后的产生LSN序号。
MasterRecord Disk 最后一个Checkpoint的LSN。

其中,一个额外的保证是在一个page写入到磁盘前,需要保证pageLsn<=flushedLSN,即需要保证数据在落地到磁盘前,其相关的日志需要被记录。

3.Transaction Execution

每个事务其实就是由写和读组成的序列,由COMMIT或ABORT结尾。

在这里,为了简化事务的执行过程中日志的变化过程,作出以下假设:

  • 1.所有日志能够放在一个page中
  • 2.page写入磁盘的操作是原子的
  • 3.单版本,由2PL作为并发协议
  • 4.对于缓存池管理器,采用STEAL(允许未提交的事务将含有修改的page写入磁盘)+NO-FORCE(不要求事务将锁修改的所有dirty page都写入到磁盘)的策略。

在事务提交的时候,执行的流程为:

  • 在日志中写入COMMIT标记。注意,这并不代表事务已经执行完并返回给用户COMMIT了,只是表示事务需要进行COMMIT的相关工作。
  • 将COMMIT标记及之前的日志都写入到磁盘中。常见的优化方法是采用两个Buffer Pool轮转,当一个Buffer Pool正在写入内容到磁盘中时,使用另外一个Buffer Pool写入新的日志。
  • 当写入到磁盘完成后,写入一个特殊标记TXN-END,然后告知外部该事务已经提交。

为了在ABORT时可以方便的回滚,每一个WAL日志需要维护一个preLSN,用于标记该事务的上一个LSN序号。在事务放弃的时候,执行的流程为:

  • 在日志中写入ABORT标记,然后告知用户该事务已经ABORT了。这里不需要等待事务的日志都写入到磁盘后再告知用户。
  • 通过preLSN获取该事务的相关日志,并利用日志中的Before Value对修改进行回滚。注意,该回滚操作也会产生日志。
  • 回滚完毕后,写入一个特殊标记TXN-END。

1.为什么需要preLSN?对于一个事务来说,他的LSN并不是连续的,因为有可能存在其他的并发事务。preLSN减少了对WAL扫描的时间。

2.为什么ABORT不需要等到WAL写入磁盘后才告诉用户ABORT?COMMIT作出该保证的原因是因为崩溃时可以知道所有的修改,而在ABORT的时候若崩溃了,我不需要知道该事务作出的修改。

4.Fuzzy Checkpointing

ARIES是允许在事务不停止的情况下,制作Checkpoint并支持恢复数据库的算法。

Fuzzy Checkpoint是ARIES所使用的制作Checkpoint的手段,在开始和结束制作checkpoint的时候,需要添加两个特殊的日志:

  • CHECKPOINT-BEGIN:表示checkpoint的起点。
  • CHECKPOINT-END:表示checkpoint制作完毕,并包含CHECKPOINT-BEGIN前的ATT和DPT。

ATT表示的是Active Transaction Table,DPT表示的是Dirty Page Table。主要是用于记录有哪些没有运行完毕的事务以及有哪些在buffer pool是dirty的。

ATT需要包含以下信息:

  • Transaction Id
  • status,如running,commiting,undo
  • lastLSN:最后创建的LSN

为何事务在结束时需要写入TXN-END?这表示的其实是在TXN-END之后,ATT中不可能会再看到该Transaction相关的信息了。

DPT则需要记录recLSN,即使page变为dirty的LSN。

Checkpoint的思想是,记录CHECKPOINT-BEGIN前的活跃事务与脏页的信息,并通过与扫描CHECKPOINT-BEGIN之后的信息归并总结,从而获得最新的活跃事务与脏页信息。

该思想其实与数据库的持久化思想相同。利用全量的数据库副本作为状态,再利用增量的追加日志作为该状态的偏移,从而得到最新的数据库状态。

5.ARIES Recovery

ARIES的恢复主要包括三个阶段:

  • Analysis:从MasterRecord中得到最后一个Checkpoint的位置,然后进行顺序扫描,得到ATT与DPT的信息。
  • Redo:从DPT中找到时间最小的LSN,以该LSN为起点,重复所有(包括ABORT)日志对应的操作。为什么ABORT的事务日志也要重复?这是因为在并发事务下,如果去除了ABORT事务的操作带来的影响,得到的结果不一定完全一致。
  • Undo:模拟Undo ABORT事务。

5.1.Analysis

Analysis做的事情就是从Checkpoint到扫描到日志尾部,从而分析出最新的ATT和DPT内容。

具体来说,是从CHECKPOINT-BEGIN顺序扫描WAL到尾部,从而维护ATT和DPT。

ATT的维护方法为:

  • 若发现了事务Tx的TXN-END,则将其从ATT中去掉。
  • 否则,若发现所有其他关于事务Tx的日志,都将其加入ATT中。

DPT的维护方法为:对于UPDATE page的语句,如果P不在DPT中,则将其写入DPT,并将其LSN作为recLSN。

5.2.Redo

系统在Redo阶段首先找出DPT中所有PageRec LSN中最小的,作为redo的起始位置,因为再往前的日志记录对应的数据修改都已落盘,不会出现在DPT中。

随后系统从redo的起始位置开始,按顺序对后续更新日志记录(包括CLR)执行redo操作,除非遇到以下三种情况,都表示该LSN对应记录已经落盘,可以直接跳过,不需要执行redo:

  • 更新的Page不在Dirty Page Table中
  • Page在Dirty Page Table中但Rec LSN大于当前LSN
  • 磁盘上的Page LSN大于当前的LSN

在redo时,系统不需要再记录日志,因为redo只是实现整个内存状态的重构,如果在redo时又出现了系统故障,则按照原来操作重新进行一遍即可。重复redo是没有风险的,因为前面三种情况已经规避了已经写入到磁盘的情况。

5.3.Undo

Undo阶段目的是撤销在系统故障时未完成的事务,即Redo阶段之后仍存在于ATT中的事务。

首先,找到ATT中lastLSN最大的事务,然后利用食物中LSN对应的日志及其preLSN所串连的日志对数据进行,直到preLSN为空。


参考与扩展

https://www.transwarp.cn/news/1712