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相关的日志记录到稳定存储器
操作系统在缓冲区管理的作用
方法一:数据库保留部分主存为缓冲区,对其管理,而不是让操作系统管理(然而几乎所有操作系统都完全控制主存区)
方法二:在操作系统的虚拟内存中实现缓冲区,会导致输出两次,一是在操作系统换页时,一是在数据库系统进行
检查点
静态检查点
- 停止接受新的事务
- 等待所有当前的活动事务提交或终止,并且在日志文件中写COMMIT或ABORT记录。
- 将日志刷新到磁盘
- 写入日志记录<CKRT>,并再次刷新记录。
- 重新开始接受新事务。
当恢复时,扫描到<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数据项,日志记录新值