• 再读simpledb 之 事务管理的实现(2)


    2、恢复管理

    2.1 日志的语义实现

    前面在Log底层实现提到,Log只负责日志在字节粒度上的实现,并不知晓日志的语义信息。日志的语义信息由恢复管理器来实现。

    image_thumb[21]

    图1 恢复管理器下日志记录类图

    LogRecord作为一个抽象了,定义了所有日志记录的基本接口;系统提供了6种不同的日志类型{CHECKPOINT,START,COMMIT,ROLLBACK,SETINT,SETSTRING}。其中SETINT和SETSTRING是真正向日志文件中写入查询操作等相关的数据,而CHECKPOINT,START,COMMIT,ROLLBACK则是为数据库恢复,向日志文件中加入的辅助信息。

    这里着重说下LogRecord中的undo。

    以SetInt为例,看下实现的代码:

    image_thumb[23]

    图2 SetInt下undo方法代码

    反做,就是用日志记录中保存的旧值,替换替换指定位置的新值。

    有两个地方要注意:

    1) –1 假LSN 系统在写入数据的时候,同时生成一条日志记录,但是LSN为-1的时候,这条日志记录只能是临时记录,不会保存到磁盘,参见下面的代码。

    image

    image

    image

    图3 LSN为-1的日志记录

    2) val 这个val在SetIntRecord对象建立的时候被初始化。SetIntRecord的构造函数有两个:

    public SetIntRecord(int txnum, Block blk, int offset, int val);// 创建一条新的SetIntRecord记录

    public SetIntRecord(BasicLogRecord rec)                            // 通过一条不含语义的字节记录,包装出一个带语义的日志记录

    显然,第二个构造函数得到的对象中,val一定是旧值

    image

    图4 RecoverMgr的SetInt方法

    同样,val是旧值。

    这样就印证了SetIntRecord中undo的作用,将对应数据复原成旧值,同时不生成新的日志记录。

    2.2 恢复算法

    标准的ARIES恢复算法:

    1) 先写日志;

    2) 重做,重现历史,然后反做,撤销未完成事务;

    3) 日志记录反做操作,避免反做“反做”的操作;

    simpledb实现了一个变种的恢复算法:只反做的恢复算法

    算法的一个前提条件是:在事务提交的时候,强行将缓存中的数据写入磁盘 这样在恢复的时候,不需要利用日志完成重做的过程;跳过ARIES算法的重做步骤;也就不需要保存更新过的日志记录的新值

    与标准的ARIES算法比较,这样的好处是:a、日志更小; b、恢复更快

    坏处是:事务提交变慢了

    所以说,这种只反做的恢复算法,适合系统不稳定,经常需要恢复的情境;当恢复很少发生时,优点不明显,而且提交效率太低,也就不适合了。

    接下来,通过代码,看下这种算法的实现。

    > commit():

    image

    图5 事务提交

    先flushAll,将事务相关的缓存片全部写入磁盘,然后,写入事务提交标记日志记录

    > rollback():

    image

    image

    图6 rollback方法

    首先将当前事务关联的脏页写入磁盘,然后回滚事务,恢复当前事务的所有操作,在日志文件中添加一条ROLLBACK的记录,并直接刷如磁盘。

    通过doRollBack可以看到回滚的实现细节:

    从日志文件倒序回溯,只查看当前事务相关的日志记录,直到遇到START停止,否则,反做该记录。

    前面提到,六种日志记录中,只有SetIntRecord和SetStringRecord的undo有实际动作,由于OO的多态特性,此处的rec.undo(txnum)在当记录为SETINT和SETSTRING的时候,隐式调用了这两个的undo,以SetIntRecord为例,在这种情况下,用的构造函数是“public SetIntRecord(BasicLogRecord rec)”,就是从日志中读出旧值,然后复位到原来的位置上。

    ------------特别地说下--------------

    通过事务完成数据写入和必要时事务回滚复原数据,这是两个相反又有些对称的操作,这里提前看下事务的写数据操作:

    image

    图7 Transaction的SetInt方法

    先利用recoveryMgr创建日志,后写入数据;看前面的图4,RecoveryMgr的SetInt方法,实际上,没有用newval做任何的操作,只是把newval要写入的地反原来保存的oldval读出来,保存到日志中。

    这与前面SetIntRecord中的undo,读出日志中的oldval,复位到指定位置,覆盖newval,回滚事务,恢复旧状态,正好是相反的一个过程

    -----------------------------------------

    > recover():

    所有的恢复操作,前面提到的只反做的恢复算法,都在这个方法中实现。

    将所有脏数据写入磁盘,然后开始恢复:

    1) 维护一个已完成事务的列表 finishedTx

    2) 倒序扫描日志文件:

               a) 如果遇到COMMIT标记,则该日志记录对应事务已经完成,不用反做,将txnum加入finishedTx

               b) 如果遇到ROLLBACK标记,则该日志记录对应事务已经反做过,相当于事务什么都没做,不需要再反做,将txnum加入finishedTx

               c) 如果遇到CHECKPOINT标记,则表明自前次系统恢复以来,所有的事务或被反做,或被持久化,本次恢复结束

               d) 如果日志记录的txnum不再finishedTx列表中,表明该日志记录对应的事务被中断,需要反做

    恢复完成后,在日志记录末尾写入一条CHECKPOINT记录,标记本次恢复操作。本条记录立即写入磁盘。

    image

    image

    图8 recover方法

  • 相关阅读:
    ZOJ 2859 Matrix Searching
    URAL 1102. Strange Dialog
    ZOJ 1986 Bridging Signals
    POJ 3233 Matrix Power Series
    POJ 1836 Alignment
    POJ 3267 The Cow Lexicon
    ZOJ 3471 Most Powerful
    IIS:HTTP 错误 403.9 禁止访问:连接的用户过多
    使用Command对象执行数据库操作
    C#类型转换
  • 原文地址:https://www.cnblogs.com/YFYkuner/p/2663686.html
Copyright © 2020-2023  润新知