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

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数据项,日志记录新值
原文地址:https://www.cnblogs.com/dirge/p/11790041.html