• 数据库·日志系统原理(恢复系统)


    ref:https://blog.csdn.net/whyangwanfu/article/details/1926367

    • Q:db-pointer(指向数据库的当前副本) 写入磁盘的时候,如果只写入部分字节后发生断电,则db-pointer不可恢复,怎么办?

    • A:磁盘系统提供了对块更新的原子性,或至少是支持磁盘扇区更新的原子性。换句话说,只要保证db-pointer存储于一个扇区,磁盘系统将保证db-pointer更新的原子性,我们可以将db-pointer存储于磁盘头来实现。

    ——《Database System Concepts》

    事务的原语操作

    在事务系统的运行当中,有三个地址空间供元素存储:

    • 磁盘空间
    • 缓冲区
    • 事务的局部地址空间。

    一个简单的读、修改X元素操作的流程如:事务到缓冲中读取元素X,如果命中,则读取事务局部地址空间并返回,如果未命中,则先将相关页从磁盘读入缓冲区。事务在它的局部地址空间中修改元素X,然后写入缓冲区,再从缓冲区写入磁盘。当然缓冲区的数据也可能不是立即拷贝入磁盘的,这取决于具体的缓冲区管理策略。
    为了便于描述,我们描述了4个操作原语:

    • INPUT(X):将包含数据库元素X的磁盘块拷贝到内存缓冲区
    • READ(X,t):将数据库元素X拷贝到事务的局部变量t。更准确地说,如果包含数据库元素X的块不在内存缓冲区中,则首先执行INPUT(X)。接着将X的值赋给局部变量t。
    • WRITE(X,t):将局部变量t的值拷贝到内存缓冲区中的数据库元素X。更准确地说,如果包含数据库元素X的块不在内存缓冲区中,则首先执行INPUT(X)。将着将t的值拷贝到缓冲区中的X。
    • OUTPUT(X):将包含X的缓冲区拷贝到回磁盘。

    日志记录

    • <Ti start>
    • <Ti, Xj, V1, V2>
    • <Ti commit>
    • <Ti abort>

    日志必须放在稳定存储器上(99.999%不会丢失)

    延迟的数据库修改redo

    • 日志仅需记录数据项的新值

    • 所有日志(包含commit)写到稳定缓存器后,再执行真正的更新write操作缩短日志记录的时间

    • 恢复时redo同时包含 <Ti start><Ti commit> 的事务

    立即的数据库修改undo + redo

    • 允许在事务仍处于活跃时,就输出到数据库

    • 日志需记录数据项的原始值与新值

    • Ti执行任何write操作前,先写日志

    • 与数据项B相关的日志写到稳定缓存器后,才执行output(B)操作

    • 恢复时同时采用redo与undo机制
      1.redo同时包含 <Ti start><Ti commit> 的事务(可能已提交事务尚未执行output操作
      2.undo只包含 <Ti start> 的事务

    缓冲区管理

    日志缓冲

    遵循先写日志规则。
    日志只允许以附加的方式写入数据。日志块最初在主存中创建,像数据块一样也由缓冲区管理,在确当的时刻,日志块会从缓冲区写入到磁盘。
    

    数据库缓冲

    虚拟内存换页的时候,会(由操作系统)输出块B1,在B1输出前,应当先输出与块B1相关的日志记录到稳定存储器
    

    操作系统在缓冲区管理的作用

    方法一:数据库保留部分主存为缓冲区,对其管理,而不是让操作系统管理(然而几乎所有操作系统都完全控制主存区)
    方法二:在操作系统的虚拟内存中实现缓冲区,会导致输出两次,一是在操作系统换页时,一是在数据库系统进行
    

    检查点

    静态检查点

    1. 停止接受新的事务
    2. 等待所有当前的活动事务提交或终止,并且在日志文件中写COMMIT或ABORT记录。
    3. 将日志刷新到磁盘
    4. 写入日志记录<CKRT>,并再次刷新记录。
    5. 重新开始接受新事务。
      当恢复时,扫描到<CKRT>日志时,就不需要继续扫描日志记录了。

    动态检查点

    静态检查点的缺点在与,可能需要很长时间等待活跃事务的完成,在用户系统看来似乎是静止了。非静态检查克服了该缺点,在做检查点时允许新事务进入。步骤如下:
    1) 写入日志记录<START CKPT(T1,……,Tk)>.其中T1,……,Tk为活跃事务。
    2) 等待T1,……,Tk每一个事务提交或终止,但允许其它事务开始。
    3) 当T1,……,Tk都已完成时,写入日志记录<END CKPT>并刷新日志。

    当系统发生故障时,从日志尾部开始扫描日志。根据扫描过程中先遇到<END CKPT>记录还是<START CKPT(T1,……,Tk)>记录,有两种情况。

    1) 如果先遇到<END CKPT>记录。所有未完成的事务在<START CKPT(T1,……,Tk)>记录后开始,只要扫描到<START CKPT(T1,……,Tk)>就不需要继续扫描了。<START CKPT(T1,……,Tk)>前的记录是可以截断的。

    2) 如果先遇到<START CKPT(T1,……,Tk)>,那么崩溃发生在检查点过程中。未完成事务只可能是到达<START CKPT(T1,……,Tk)>前遇到的那些,以及T1,……,Tk在崩溃前未完成的那些。因此只要继续扫描到未完成事务中最早的那个事务的开始就行了。
    一个通常的规律是,一旦<END CKPT>记录到了磁盘,我们就可以将上一个<START CKPT>记录前的日志删除了。

    对比

    都是先output日志再output数据库元素
    undo:每write一条日志,write一次数据项更新,日志记录旧值,
    redo:write完所有日志,再延迟更新write数据项,日志记录新值
  • 相关阅读:
    模块三
    python的6种基本数据类型--字典
    python的6种基本数据类型--集合
    数据集之集合
    锚点使用
    JSON返回DateTime/Date('123123123')/解决办法
    JSON返回DateTime/Date('123123123')/解决办法
    设计模式-系列索引
    设计模式-系列索引
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/dirge/p/11790041.html
Copyright © 2020-2023  润新知