设置检查点是一个开销很大的操作,特别是该操作涉及到向稳定存储中写入状态,需要寻找一种减少检查点的数目但是还允许恢复的技术。
目标:使交易可靠
- ……在出现故障时
- 机器可能会崩溃。 磁盘内容(OK),内存(易失性),机器不会行为不端
- 网络很脆弱,丢包,处理超时
- 如果我们将数据库状态存储在内存中,崩溃将导致“耐久性”的丢失。
- 可能违反原子性,即恢复未提交的事务COMMIT或ABORT。
通常思路:将足够的信息存储到磁盘以确定全局状态(以LOG的形式)
挑战
- 磁盘性能差(vs内存)
- 无法将所有事务保存到磁盘
- 内存通常快几个数量级
- 写入磁盘来处理任意崩溃是很困难的
arbitrary crash:服务器可能在随意的时间产生随意的- 有几个原因,主要是HDD和SSD都有缓冲区(缓冲区往往是不稳定的)
- 相同的概念:在磁盘上存储足够的数据以便在崩溃后恢复到有效状态:
- 影子页面和预写日志记录(WAL)
Shadow Pages
- 提供原子性和耐久性,“页面”=存储单元
- 想法:编写页面时,制作一个“影子”副本
- 没有来自其他页面的引用,很容易编辑!
- ABORT:放弃影子页面
- 基本上是“写入时复制”以避免原地更新页面
Write-Ahead-Logging
- 提供原子性和耐久性
- 想法:创建一个记录每次更新数据库的日志
- 存储在磁盘上时认为可靠的更新
- 更新后的版本保存在内存中(页面缓存)
- 日志通常存储REDO和UNDO操作
- 崩溃后,通过重播日志条目来重建正确的状态
- WAL更常见,磁盘操作更少,一旦写入日志就认为提交的事务。
- 按条目顺序查看序列号
- 日志序列号(LSN)
- 数据库:固定大小的PAGES,页面级存储
- 磁盘上的页面,一些也在内存中(页面缓存)
- “脏页面”:内存中的页面与磁盘上的不同
- 重建全球一致的状态
- 日志文件+磁盘内容+(页面缓存)
- 日志由记录序列组成
- Begin LSN, TID #Begin TXN
- End LSN, TID, PrevLSN #Finish TXN (abort or commit)
- Update LSN, TID, PrevLSN, pageID, offset, old value, new value
- 交易表(TT):所有TXNS未写入磁盘
- 包括他们造成的最后一个日志条目的Seq Num
- 脏页表(DPT):内存中的所有脏页
- 修改页面,但不写回磁盘。
脏页面表记录了所有已修改但尚未写回光盘的页面以及导致页面变脏的第一个序号。 事务表包含当前正在运行的所有事务以及它们引起的最后一个日志条目的序列号。
我们创建表单的日志记录(序列号,交易ID,页面ID,重做,撤消,以前的序列号)。 重做和撤消字段保留有关此日志记录保存的更改以及如何撤消它们的信息。 以前的序列号是对此事务创建的前一个日志记录的引用。 在中止事务的情况下,可以使用先前序列号以相反顺序遍历日志文件,撤销在特定事务中采取的所有操作。
- Commit a transaction
- 日志文件是最新的,直到提交输入
- 不要更新实际的磁盘页面,日志文件有信息
- 将日志文件的“尾部”保留在内存中=>不提交
- 如果尾部被清除(崩溃),则部分执行的交易将会丢失。 仍然可以恢复到可靠状态
- Abort a transaction
- 找到来自TT的最后一个条目,撤消迄今为止的所有更新
- 使用PrevLSN恢复内存页面以开始TXN
- 如果磁盘上的页面需要撤消,请等待(回到此处)
Recovery using WAL – 3 passes
- Analysis Pass
- 重建TT和DPT(从开始或上一个检查点开始)
- 在开始时获取所有页面的副本
- Recovery Pass (redo pass)
- 重播日志,更新所有脏页面
- 在事故发生时把所有事情都带到一个状态
- Undo Pass
- 向后回放日志文件,恢复未提交的事务所做的任何更改(使用PrevLSN)
- 对于每个写入补偿日志记录(CLR)
- 一旦你到达BEGIN TXN,写一个END TXN条目