第七章 事务处理、并发和恢复
第七章 事务处理、并发和恢复
主要内容:数据库事务的概念,事务的ACID特性,基于锁的并发协议,死锁处理,多版本机制,快照隔离
1) 基本要求
- 了解事务的基本概念
- 熟悉事务的ACID特性
- 了解并发控制和多版本机制的基本原理
2) 重点、难点
重点: 事务的ACID特性
难点: 基于WAL的恢复机制
主要内容:数据库事务隔离性要求的具体实现-并发控制
1) 基本要求
- 了解并发控制和多版本机制的基本原理
2) 重点、难点
重点: 并发控制的机制
难点: 2阶段锁
主要内容:数据库事务原子性和持久性的要求和具体实现-日志和恢复
1) 基本要求
- 了解并日志和恢复的基本原理,及对原子性、持久性的支撑
2) 重点、难点
重点: WAL、redo、undo
难点: 基于日志的恢复
事务
事务的定义:
- 访问并可能更新各种数据项的一个程序执行单元
- 事务是多个数据库操作组合成的一个不可分割的、同时成功或失败的工作单元
- 显示: COMMIT/ROLLBACK
隐式: 由DBMS自动划分 - E.g. 账户A向账户B转账50元 (A=A-50, B=B+50)
用户A向12306订G01次车票一张 (A=A+1, G01=G01-1) - 事务具有四个特性(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)
事务的要求:
事务保持正确性的评判准则
原子性(atomicity):不可分割,一个事务包含的所有操作“要么全都执行,要么全都不执行”
一致性(consistency):正确一致,事务的执行不改变数据库实例的一致性 ,即事务执行前数据库满足一致性,执行后也满足一致性
隔离性(isolation):互不干扰,事务之间相互隔离,即对并发执行的其他事务无感知 (多个事务在并发执行的过程中所得到的结果,和串行执行得到的结果是一致)
持久性(durability):永久保持,一旦某个事务执行完成,其对数据的更改将持久化反应到数据库实例上
原子性(Atomicity)
不可分割,一个事务包含的所有操作“要么全都执行,要么全都不执行”
事务状态:
- 活跃(active): 事务开始执行时的初始状态
- 部分提交(partially committed): 最后一条语句被执行后,提交之前
- 失效(failed) : 发现该事务不能再继续正常执行了
- 中止(aborted) : 事务已经回滚,数据库实例已恢复到事务开始前的状态,中止之后可选择 重启事务 和 杀死事务 操作
- 提交(committed) : 事务成功完成之后
原子性只允许事务最终处于 提交态(全都执行) 或 中止态(全都不执行)
保障原子性的机制 :
- 日志
- DBMS用日志记录所有的操作
- 事务一旦中止,可以回溯日志以回滚操作
- 影子分页技术
- 对页面拷贝
- 事务优先在拷贝页面上执行
- 仅当执行提交(commit) 后,才持久化可见
一致性(consistency)
正确一致,事务的执行不改变数据库实例的一致性,即事务执行前数据库满足一致性,执行后也满足一致性
显式的完整性约束
隐式的完整性约束:E.g. SUM(R.收入)-SUM(R.支出)=SUM(R.利润)
事务执行前:数据库实例应满足一致性要求 E.g. 已出售票量+剩余票量=总票量
事务执行中:允许暂时的不一致
事务执行完(无论成功与否):数据库实例应满足一致性要求 E.g. 已出售票量+剩余票量=总票量
如果不一致,说明事务执行有问题
保证事务一致性应是上层应用的职责,即语句编写者
隔离性(isolation)
互不干扰,事务之间相互隔离,即对并发执行的其他事务无感知 (多个事务在并发执行的过程中所得到的结果,和串行执行得到的结果是一致)
事务的穿插执行可能破坏一致性 / 并发执行产生冲突
并发控制是实现事务隔离性的手段
调度:多个并发事务的操作穿插执行的顺序
串行化调度:串行执行 如果调度S没有对其中各事务的操作进行穿插执行(即调度S严格将事务依次执行),那么称调度S为串行的
可串行化调度:如果一个调度S等价于另一个串行化的调度S’,那么这个调度S称为可串行化的
调度等价:
- 冲突可串行化
- 冲突:分属于两个事务,访问同一数据对象,至少一个为写
- 读-写冲突(不可重复读):事务1读取数据后,事务2执行更新(增删改)操作,使事务1无法再现前一次读取结果
- 写-读冲突(读“赃”数据):事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1由于某种原因被撤消。这时事务1已修改过的数据恢复原值,事务2读到的数据就与数据库中的数据不一致,是不正确的数据,又称为“脏”数据
- 写-写冲突 (丢失修改):事务1与事务2从数据库中读入同一数据并修改,事务2的提交结果破坏了事务1提交的结果,导致事务1的修改被丢失
- 冲突等价:交换无冲突的操作的次序
- 冲突可串行化:当调度S 与另一串行调度是冲突等价的,则称调度S是冲突可串行化的
- 冲突:分属于两个事务,访问同一数据对象,至少一个为写
- 视图可串行化
利用优先图进行冲突可串行化测试
无环 链式 <=> 冲突可串行化
隔离级别(由高到低,一步步放宽)
有些应用场景可允许不可串行调度的执行
- 可串行化
- 可能出现的问题:无
- 可重复读:只允许读取已提交的数据(不会出现脏读),不允许一个事务两次读取之间其他事务对其更新(不会出现不可重复读 因此 可重复读)
- 可能出现的问题:丢失修改
- 已提交读:只允许读取已提交的数据(不会出现脏读),但并不要求可重复读(读间可更新)(会出现不可重复读)
- 可能出现的问题:丢失修改 + 不可重复读
- 未提交读:允许读取未提交数据
- 可能出现的问题:丢失修改 + 不可重复读 + 脏读
持久性(durability)
永久保持,一旦某个事务执行完成,其对数据的更改将持久化反应到数据库实例上
保障持久性的机制
-
日志
-
影子分页技术
两阶段锁协议(2PL)
互斥锁(X):既可读又可写
共享锁(S):只可读不可写
每个事务根据自己的操作类型申请数据项上对应的锁
锁管理器:管理事务对于锁的申请和授予
事务仅当被授予数据项的对应锁,才可访问该数据项
朴素的加锁:不能确保串行化
延迟解锁:死锁、饿死
两阶段封锁协议(2PL):
2-PL是冲突可串行化的充分非必要条件, (一个调度)所有事务均满足2-PL协议=>冲突可串行化 ,存在一些冲突可串行化的调度并不能通过2-PL协议获得
2-PL不能保证没有死锁
2-PL协议(允许锁转换):
- 阶段1: 增长阶段
事务获取锁,但不能释放锁
事务可从S锁升级至X锁 - 阶段2: 缩减阶段 (要等锁增长完才能进入阶段2)
事务可以释放锁,但不能获得新锁
事务可从X锁降级至S锁
给数据表加锁是说锁定整张表
日志和恢复
崩溃恢复策略设计
持久性: 如果在崩溃时刻一个已提交的事务对数据库的修改没有从缓冲区写回磁盘,那么事务的持久性受到了影响,需要重做这些事务
- Force(强制):事务每次修改都强制写入磁盘,保证持久性(但是消耗系统资源)
- No-Force(非强制): 事务每次修改不强制写入磁盘,但是会影响持久性。因此在事务提交之前,将该事务所有对数据库的影响以日志的形式写回磁盘 崩溃会重做日志
原子性: 如果在崩溃时刻一个未结束的事务对数据库的修改有一部分已经从缓冲区写回磁盘,那么事务的原子性受到了影响,需要回滚这些事务
- NO-STEAL(非窃取):只要事务还没提交,它的一切修改都不写入磁盘,保证原子性(但是占用大量缓冲区空间)
- STEAL(窃取): 未提交事务的修改也可以写入磁盘,影响原子性。因此在事务将修改写入磁盘之前,先向磁盘写入可以撤销修改的回滚日志
预写日志(WAL):每次更改数据之前,先在日志中添加对应的记录 ;即日志在数据页之前刷新
当数据 修改数据项 (Write(X))时产生
重做日志: 记录事务对数据库的所有影响,保存更新以后的值,恢复子系统从前往后扫描重做日志
回滚日志: 记录撤销事务所需的内容,保存修改以前的值,恢复子系统从后往前扫描重做日志
没有提交和中止的事务要undo回滚 (找没有commit的)
有提交或中止的事务要redo重做(找已经commit的)
检查点:
《数据库原理》课程笔记: