Review 11 | Transactions
Basic
Failure Classification
数据库系统可能出现的故障
- 发生在数据库应用里的是事务故障出现在单个事务里,比较局部,一般直接rollback
- DBMS和OS的故障就是系统崩溃,是全局性的,一堆事务都会受影响,也需要日志去恢复
- 比如死锁需要牺牲一个事务,我们称这个被牺牲的事务发生了系统故障,这不是他自己的问题;还有突然断电也是系统故障
- Database的故障就是磁盘故障,解决思路就是多备份,还是得借用日记去恢复
Storage Structure
- Volatile storage
- not survive system crashes, like main memory, cache memory
- Nonvolatile storage
- survives system crashes, like disk, tape, flash memory
- Stable storage
- 理想化的存储结构,100%不会出问题,只存在于理论中
Log-Based Recovery
log 存储在 stable storage(稳定存储器),是一个序列/线性文件,包含的元素是 log records,记录各种会影响数据库的操作信息,特别是 update
- \(T_i\) 开始会记录
<Ti start>
Ti
是这个事务的 id,每个事务的标识符
- 执行
write(X)
操作前,日志会先记录<Ti, X, V1, V2>
,V1
旧,V2
新 - 事务完成最后一个statement后,记录
<Ti commit>
- 事务要 rollback ,记录
<Ti abort>
恢复现场(redo pass)repeating history(重复历史),没有 commit 的或没有 abort 的事务就放进 undo list,undo pass 撤销 undo list 的事务已经执行的操作
Write-Ahead Logging or WAL(先写日志原则),事务commit结束的标志是其对应的log commit记录写入了stable storage
Checkpoints
设置检查点的操作如下:
- 先让目前所有还在主存的日志都写入 stable storage
- 再让所有目前主存里已修改的block写出去,保证前面的操作已经全部写入了数据库
- 日志记录
<checkpoint L>
,L 是此时正在运行的事务表,包含所有正在进行的事务
设置检查点时会中断所有的 update
操作
Fuzzy Checkpointing
模糊的检查点,只刷日志不刷脏数据,会记录此时哪些block是脏的,以此减少写检查点的stall
多一个指向检查点的指针 last_checkpoint ,等一个检查点所有被记录的脏数据都被写出去了,last_checkpoint 就会指向这个检查点
Recovery with Early Lock Release and Logical Undo
不是严格两阶段锁协议的情况,undo 需要使用 logical Undo ,如果你加了300,那么我就减 300,而不是直接用旧数据覆盖
操作的 log record 是从 undo 角度记录的,还没end就crash的操作还是只能用物理undo实现
ARIES Recovery Algorithm
ARIES Database
每个日志record都有一个严格单增的 Log sequence number (LSN) ,类似id
- 左下角就是磁盘/数据库,里面我们假设有四个 page
P1 17
表示 P1 这个 page 已经被写入pageLSN
=17 对应的操作需要写的数据
- 右边的大表就是 \(log\; file\),同一个颜色对应同一个事务
PrevLSN
指向同一事务上一个recordTNO
就是事务 idTYPE
指示该record 对应的操作类型,u
就是update
PID
指示操作的页,与len
和offset
共同指示要修改的数据段位置before
和after
即修改前后数据- checkpoint 只保存 Dirty Page Table 和 当前事务表(Active TXN Table)
- 左边的 \(Active\; TXN\; Table\) 即当前事务表
- 记录目前的活跃的事务中最新的已执行的操作
- 上面的 \(Buffer \;Pool\) 即缓冲池,存放还没被写入数据库的脏数据
- 对应的数字就是已经执行好的操作,但还没写入数据库
- 左上角的 \(Dirty\; Page\; Table\) 记录脏数据,已经这个脏数据哪些操作造成的修改还没被写入数据库
- 记录当前的 buffer pool 里哪些是脏数据
PageLSN
记录最新的已执行操作,RecLSN
记录最远的还没写入数据库的操作- 比如,我们只关注page P1,其相关的操作共有
LSN
=15,17,20,22 四个,15和17已经写入数据库了,20和22还没写入,还滞留在缓冲里,可能会丢失
此外,还有 redo-only log record called compensation log record (CLR) ,用来记录永远不需要被 undo 的恢复操作,其有一个 UndoNextLSN
用于标记上一条需要被 undo 的record,这中间的 record 都是已经undo完成了的
UndoNextLSN 用于 undo 过程中出故障的情况,这样重新开始恢复时就不需要 undo 已经 undo 过的了
Aries Recovery: 3 Passes
Analysis pass
Analysis pass 从最后一个检查点出发,读取里面的脏页表,找出最小的 RecLSN
作为 redo 的起点,记为 RedoLSN
;如果没脏表就用检查点的 LSN
关于构建 undo-list
,先把检查点里的当前事务表 ATT 包含的事务全部放进去,同时读取这些事务目前读取到的最新的 LSN
,并在后面的分析中不断更新,这个会在 undo 时用到
然后从检查点开始往下找,发现有 record 对应的事务不在 undo-list
的,就把对应的事务放进去
如果发现有 update
类型的 record,就看其写的 page 在不在脏页表,如果不在就将这个 page 放入脏页表,并将 RecLSN
设置为相关的最新 LSN
如果发现事务执行结束的 record,就将这个事务移出 undo-list
执行到log最后,分析就结束了
这时,你得到了 RedoLSN
用于确定从哪开始 redo pass,脏页表和 RecLSN
用于减少 redo工作量,undo-list
的事务都要被回滚
Redo pass
scan forward 是指 log file 往下读,该死的洋文
从 RedoLSN
开始,检查 update
record,如果写的 page 不在脏页表就不用管,如果 LSN
小于 RecLSN
也不用管,如果 pageLSN
大于 record LSN
,也不用管,否则就要 redo
Undo Pass
redo 完按照 undo-list
里现有的事务,准备进行 undo
undo 是一个事务一个事务来的,先undo完一个事务再undo下一个事务,undo顺序按照各事务最后一条record的LSN从近到远
我们给 undo-list
的每个事务都设置一个所谓的 next LSN,指向这个事务下一条要 undo record,然后我们每次 undo 的是 next LSN 中最新的那个
analysis 时我们找出了 undo-list
里每个事务的最新的 LSN 用于初始化 next LSN
我们先从 undo-list
里最新的日志开始 undo,如果遇到了 CLR,就跳转到 UndoNextLSN 进行 undo,如果不是 CLRs,只是普通的 record,我们就将下一条需要 undo 的日志设置为这条日志的 PrevLSN,同时写一条对应的 CLR,UndoNextLSN 即 PrevLSN,知道事务的开头
然后 undo 下一个事务,直到全部 undo 完
最后是个恢复例子: