数据库实现——系统故障对策

版权声明:本文参考的是CSDN博主「Hello Hunk」的原创文章
原文链接:https://blog.csdn.net/u012299594/article/details/90740809

1 概述
1.1 数据库是如何访问数据的?

数据库系统常驻于非易失性存储器(主要是磁盘),在任何时间都只有部分内容在主存中。数据库分成称为块(block)的定长存储单位。块是磁盘数据传送的单位,可能包含多个数据项。事务由磁盘向主存输入信息,然后再将信息输出回磁盘。输入和输出操作都以块为单位。位于磁盘上的块为物理块,临时位于主存中的块称为缓冲块。内存中用于临时存放块的区域叫做磁盘缓冲区。

下面这张图描述了事务操作数据时的大致过程:
在这里插入图片描述事务的原语操作:
input(A):当事务T要读A,而A的块不在缓冲区中,则需要缓冲管理器执行input命令。将包含数据库元素A的磁盘块拷贝到主存缓冲区。
read(A,t):将数据库元素A的值拷贝到事务T的地址空间中的局部变量t。
write(A,t):将局部变量t的值拷贝到主存缓冲区中的数据库元素A中。(如果数据库元素A的磁盘块不在主存缓冲区,也要先执行input)
output(A):将包含A的缓冲区中的块拷贝回磁盘。

如果数据库系统在write(B)完成后和output(B)完成之前(写到了缓冲区而未写到磁盘)发生故障,事务已经提交的修改就会丢失。实际上,更大概率的事件是在事务提交之前就遇到了系统故障,而此时事务的部分更新已经写回磁盘,部分更新没有完成,破坏了磁盘数据的一致性。

因此,需要设计一些机制来保证数据据系统即使发生崩溃,事务的原子性也能得到保证,从而保证数据的一致性。

2 故障恢复的基础

在系统重启后,想要做一些恢复动作来保持原子性,就必须在修改数据库之前,先写入一些信息来记录后面的修改,这种工作也叫作预埋。使用最为广泛的记录数据库修改的结构就是日志,日志是日志记录的序列,它记录数据库中的所有更新活动。

恢复的原理十分简单,可以用一个词来概括:冗余。
建立用于数据的最常用的技术是:数据转储和登记日志文件。

下图给出了恢复管理器。当发生崩溃时,恢复管理器就被激活。它检查日志并在必要时利用日志恢复数据。
日志管理器与事务管理器事务运行的数据结构:

idstatus
lockslogs

2.1 日志记录

每次事务执行写操作时,必须在数据库修改前建立该次写操作的日志记录并把它加到日志中,而日志必须存放在稳定存储器中。这里需要注意的是,数据据系统所在的磁盘属于非易失性存储,也容易发生各种物理故障,比如磁头和扇区损坏。而日志所在的存储需要更高的稳定性,稳定存储器中的信息永远不会丢失(接近100%可靠),一般就是采用RAID技术来尽可能保证磁盘单点故障不会损坏到数据。

日志记录的结构:<T, X, v>

它记录了某个事务在某个数据项上的修改,并保存了修改前的旧值。事务T对数据元素X进行了修改,修改前的值为v。

日志中还包括一些事务状态的记录:

<Ti, ,start>      事务开始
<Ti, commit>   事务提交 ,这是事务的最后一个日志记录,当这条记录输出到稳定性存储后,就可以说这个事务提交了,这时所有更早的日志记录都已经输出到稳定性存储中,即使发生崩溃,事务所做的更新也可以重做(redo)。如果系统发生在<Ti, commit>输出到稳定性存储之前,事务Ti将会回滚(undo)。
<Ti, abort>      事务中止

需要强调的是,日志记录输出到稳定存储器也是异步的,和数据更新一样仍然会先写入系统缓冲块,再在合适的时候写入到稳定存储器。
2.2 事务的redo和undo

恢复管理器的第一个任务就是将事务划分为已提交事务和未提交事务。(这一点在undo的恢复中深有体会,从后往前扫描,找出两类事务,已提交的不用考虑,未提交的执行undo操作)发生系统崩溃后,系统查询日志以确定为保证原子性需要对哪些事务进行重做(redo),对哪些事务进行撤销(undo)。

(1)undo将事务更新过的所有数据项的值都恢复成旧值。

规则:先写好日志记录<T,X,v>(v是旧值),才可执行数据元素的output(),最后执行
只要见不到提交<Ti, commit> 或者终止<Ti, abort>,就将操作撤销,即将undo日志内的东西恢复<T,A,v>(T事务对数据库元素A进行操作,操作前值为v)对于事物的undo操作完成后,它写一个<Ti, abort>日志记录,表示撤销完成了。

检查点checkpoint

如果每次系统崩溃后的恢复工作都需要扫描整个日志来完成,会导致:

1.搜索过程太耗时。
2.对于大部分事务,已经完成缓冲块到磁盘块的输出操作,不存在破坏原子性的问题,对它们重做会使恢复过程加长。

实际上,需要重做或者撤销的事务,只是那些在系统崩溃时正处于活跃状态的事务(包括未提交的和正在回滚的事务)。因此引入checkpoint来改善恢复效率,降低恢复的开销。
检查点分类:①静止检查点 ②非静止检查点

①静止检查点的执行过程:

1.数据库停止接收新的事务。
2.等到当前活跃的事务提交或者中止,并且在日志中写入了commit或者abort记录。
3.将日志刷新到磁盘。
4.写入日志记录<CKPT>,并再次刷新日志。*此时检查点已经设置成功*
5.重新开始接收事务。

在系统崩溃发生后,系统检查日志找到最后一条记录(从尾部反向搜索)后,只需要对L中的事务执行redo或者undo。因为只需要从最后的checkpoint位置处开始执行恢复,效率会提高很多。同时,做完checkpoint后,之前的日志记录也就不再需要了,数据据系统可以在需要更多空间时清除这些日志记录。

然而,静止检查点存在一个问题:添加检查点的时候我们必须关闭系统,正活跃的事务可能需要很长时间来提交或者中止,此时用户看来系统似乎停止了,所以我们引入非静止检查点。

②非静止检查点的执行过程:
1.写入日志记录<start CKPT(T1,T2……)>并刷新日志,T1,……是所有正在活跃的事务。
2.等待T1……中的事务提交或者中止,但是在这个过程中,允许其他事务开始。
3.当T1,……都完成时,写入并刷新日志。
在这里插入图片描述 假设崩溃发生在检查点过程当中,崩溃后日志的结尾如图17.6所示。从后往前扫描,<T3,E,25>T3是未完成事务,T1是已完成事务,……往上找到了<START CKPT(T1,T2)>,在找到它的上一个CKPT即可停止。对未完成事务进行undo操作即可。

(2)redo将事务更新过的所有数据项的值都设置成新值。

规则:先写好日志记录<T,X,v>(v是新值),日志执行完以后,才可以执行数据元素的output()
日志中没见到,则数据库所做的更新就一定还没有写到磁盘上。
redo日志恢复步骤:
1.确定已经提交的事务。(事务的数据结构,status:进行,提交)
2.从首部开始扫描日志。对每一条遇到的记录<T,X,v>:
(a)如果T是未提交的事务,则什么也不做。
(b)如果T是已经提交的事务,则为数据库元素X写入v
3.对每个未完成的事务T,在日志中写入一个记录并刷新日志。

对于恢复算法,一般都是对日志进行扫描,在扫描过程中每遇到一个redo日志就执行redo动作,而不是针对每个事务单独做redo,这样可以提高恢复的效率。

如果日志包括<Ti, ,start>,以及<Ti, commit> 或者<Ti, abort>,执行redo。如果有<Ti, abort>,说明事务中止完成,为什么还要执行redo?<Ti, ,abort>日志记录是事务回滚时写入的,日志中一定也包含了回滚时写入的只读日志记录<T, X, v>,所以发现<Ti, ,abort>时重做是对事务Ti的修改做了撤销,这里确实是冗余操作,但是简化了恢复算法。
检查点checkpoint
检查点执行过程:
(a)写入日志记录<START CKPT(T1,T2,……)>,其中T1,……是所有活跃的事务,并刷新日志。
(b)将START CKPT记录写入日志时已提交事务已经写到缓冲区,但是还没有写到磁盘的数据库元素写到磁盘。(控制检查点开始的时候只有活跃元素,其他都已完成。所以恢复时,前边的内容一定没问题,不需要看)
(c)写入日志记录并刷新日志。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值