数据库系统概论-第十一章-并发控制
DBMS 必须提供并发控制机制,并发控制机制是衡量一个 DBMS 性能的重要标志之一
并发控制机制的任务:
- 对并发操作进行正确调度
- 保证事务的隔离性
- 保证数据库的一致性
并发操作带来的数据不一致性:
- 丢失修改( lost update)
- 不可重复读( non-repeatable read)
- 读 “脏 ”数据( dirty read )
1. 丢失修改
丢失修改是指事务1 与事务 2 从数据库中读入同一数据并修改,事务 2 的提交结果破坏了事务 1 提交的结果,导致事务 1 的修改被丢失。
2. 不可重复读
不可重复读是指事务1 读取数据后,事务2执行更新操作,使事务1 无法再现前一次读取结果。
事务 1 读取某一数据后:
1. 事务 2 对其做了修改,当事务 1 再次读该数据时,得到与前一次不同的值。
2. 事务 2 删除了其中部分记录,当事务1 再次读取数据时,发现某些记录神密地消失了。
3. 事务 2 插入了一些记录,当事务 1 再次按相同条件读取数据时,发现多了一些记录。
后两种不可重复读有时也称为幻影现象( phantom row )
3. 读 “脏 ”数据
事务 1 修改某一数据,并将其写回磁盘
事务 2 读取同一数据后
事务 1 由于某种原因被撤消,这时事务 1 已修改过的数据恢复原值
事务 2 读到的数据就与数据库中的数据不一致,是不正确的数据,又称为 “脏 ”数据。
8.2 封锁
一、什么是封锁
封锁就是事务 T 在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁
加锁后事务 T 就对该数据对象有了一定的控制, 在事务 T 释放它的锁之前, 其它的事务不能更新此数据对象。
封锁是实现并发控制的一个非常重要的技术
二、基本封锁类型
8.3 封锁协议
在运用 X 锁和 S 锁对数据对象加锁时,需要约定一些规则:封锁协议( Locking Protocol )
- 何时申请 X 锁或 S 锁
- 持锁时间、何时释放
- 不同的封锁协议,在不同的程度上为并发操
- 作的正确调度提供一定的保证
常用的封锁协议:三级封锁协议
1 级封锁协议
事务 T 在修改数据 R 之前必须先对其加 X 锁,直到事务结束才释放
正常结束( COMMIT )
非正常结束( ROLLBACK )
1 级封锁协议可防止丢失修改
在 1 级封锁协议中, 如果是读数据, 不需要加锁的, 所以它不能保证可重复读和不读 “脏 ”数据。
2 级封锁协议
1 级封锁协议 +事务 T 在读取数据 R 前必须先加 S 锁,读完后即可释放 S 锁
2 级封锁协议可以防止丢失修改和读 “脏”数据。
在 2 级封锁协议中,由于读完数据后即可释放 S 锁,所以它不能保证可重复读。
3 级封锁协议
1 级封锁协议 + 事务 T 在读取数据 R 之前必须先对其加 S 锁,直到事务结束才释放
3 级封锁协议可防止丢失修改、读脏数据和不可重复读。
8.4 活锁和死锁
1. 死锁的预防
( 1)一次封锁法
要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行一次封锁法存在的问题:降低并发度
扩大封锁范围
将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度难于事先精确确定封锁对象
数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难事先精确地确定每个事务所要封锁的数据对象
解决方法:将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度。
( 2)顺序封锁法
顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。
顺序封锁法存在的问题:
- 维护成本高:
数据库系统中可封锁的数据对象极其众多,并且随数据的插入、删除等操作而不断地变化,要维护这样极多而且变化的资源的封锁顺序非常困难,成本很高
- 难于实现:
事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序去施加封锁。
2. 死锁的诊断与解除允许死锁发生解除死锁
由 DBMS 的并发控制子系统定期检测系统中是否存在死锁一旦检测到死锁,就要设法解除
检测死锁:
- 超时法:
如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
优点:实现简单
缺点: 有可能误判死锁
时限若设置得太长,死锁发生后不能及时发现
- 等待图法:
用事务等待图动态反映所有事务的等待情况
事务等待图是一个有向图 G=(T,U )
T 为结点的集合,每个结点表示正运行的事务
U 为边的集合,每条边表示事务等待的情况
若 T1 等待 T2,则 T1, T2 之间划一条有向边,从 T1 指向 T2
并发控制子系统周期性地(比如每隔 1 min )检测事务等待图,如果发现图中存在回路,则表
示系统中出现了死锁。
8.5 并发调度的可串行性
几个事务的并行执行是正确的,当且仅当其结果与按某一次序串行地执行它们时的结果相同。
这种并行调度策略称为可串行化( Serializable)的调度。
可串行性是并行事务正确性的唯一准则
保证并发操作调度正确性的方法
封锁方法:两段锁( Two-Phase Locking ,简称 2PL)协议
时标方法
乐观方法
8.6 两段锁协议
两段锁协议的内容:
- 1. 在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁
- 2. 在释放一个封锁之后,事务不再获得任何其他封锁。
“两段 ”锁的含义
事务分为两个阶段:
- 第一阶段是获得封锁,也称为扩展阶段;
- 第二阶段是释放封锁,也称为收缩阶段。
并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的。
所有遵守两段锁协议的事务,其并行执行的结果一定是正确的,事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件可串行化的调度中,不一定所有事务都必须符合两段锁协议。
两段锁协议与三级封锁协议
两类不同目的的协议
两段锁协议
保证并发调度的正确性
三级封锁协议
在不同程度上保证数据一致性
遵守第三级封锁协议必然遵守两段协议
8.7 封锁的粒度
一、什么是封锁粒度
X 锁和 S 锁都是加在某一个数据对象上的封锁的对象 :逻辑单元,物理单元
逻辑单元 : 属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等
物理单元:页(数据页或索引页) 、物理记录等
二、选择封锁粒度的原则
封锁的粒度越 大,小,
系统被封锁的对象 少,多,
并发度 小,高,
系统开销 小,大,
选择封锁粒度:
考虑封锁机构和并发度两个因素,对系统开销与并发度进行权衡:
- 需要处理多个关系的大量元组的用户事务:以数据库为封锁单位;
- 需要处理大量元组的用户事务:以关系为封锁单元;
- 只处理少量元组的用户事务:以元组为封锁单位
8.7.2 多粒度封锁
多粒度树:
- 以树形结构来表示多级封锁粒度
- 根结点是整个数据库,表示最大的数据粒度
- 叶结点表示最小的数据粒度
- 显式封锁和隐式封锁
- 显式封锁 : 直接加到数据对象上的封锁
- 隐式封锁 : 由于其上级结点加锁而使该数据对象加上了锁
- 显式封锁和隐式封锁的效果是一样的
8.7.3 意向锁
引进意向锁( intention lock )目的:
- 提高对某个数据对象加锁时系统的检查效率
- 对任一结点加基本锁,必须先对它的上层结点加意向锁
- 如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁