第8章 并发控制 概述 封锁 封锁协议 活锁和死锁 并发调度的可串行性 两段锁协议 封锁的粒度 Oracle的并发控制 2019/11/20 数据库原理
概 述 多事务执行方式 (1)事务串行执行 每个时刻只有一个事务运行 不能充分利用系统资源,发挥数据库共享资源的特点。 (2)交叉并发方式(interleaved concurrency) 事务的并行执行是这些并行事务的并行操作轮流交叉运行 单处理机系统中的并发方式,能够减少处理机的空闲时间,提高系统的效率。 (3)同时并发方式(simultaneous concurrency) 多处理机系统中,每个处理机可以运行一个事务,多个处理机可以同时运行多个事务,实现多个事务真正的并行运行。 最理想的并发方式 更复杂的并发方式机制 2019/11/20 数据库原理
事务并发执行带来的问题 可能会存取和存储不正确的数据,破坏事务的隔离性和数据库的一致性。 DBMS必须提供并发控制机制 2019/11/20 数据库原理
8.1 并发控制概述 并发控制机制的任务 对并发操作进行正确调度 保证事务的隔离性 保证数据库的一致性 2019/11/20 数据库原理
数据不一致实例:飞机订票系统 T1的修改被T2覆盖了! 读A=16 A←A-3 写回A=13 ① 读A=16 ② ③ A←A-1 读A=16 A←A-3 写回A=13 ① 读A=16 ② ③ A←A-1 写回A=15 ④ 事务 T2 事务 T1 T1的修改被T2覆盖了! 2019/11/20 数据库原理
并发操作带来的数据不一致性 丢失修改(lost update) 不可重复读(non-repeatable read) 读“脏”数据(dirty read) 丢失修改是指事务1与事务2从数据库中读入同一数据并修改,事务2的提交结果破坏了事务1提交的结果,导致事务1的修改被丢失。 不可重复读是指事务1读取数据后,事务2执行更新操作,使事务1无法再现前一次读取结果。 事务1修改某一数据,并将其写回磁盘;事务2读取同一数据后;事务1由于某种原因被撤消,这时事务1已修改过的数据恢复原值;事务2读到的数据就与数据库中的数据不一致,是不正确的数据,又称为“脏”数据。 2019/11/20 数据库原理
(a) 丢失修改 (b) 不可重复读 (c) 读“脏”数据 T1 T2 ① 读A=16 ② ③ A←A-1 写回 A=15 ④ 读A=16 ② ③ A←A-1 写回 A=15 ④ 读A=16 A←A-1 读B=100 B←B*2 写回B=200 ① 读A=50 求和=150 ② ③ 读A=50 读B=200 求和=250 (验算不对) T2 T1 读C=200 ① 读C=100 C←C*2 写回C ② ③ ROLLBACK C恢复为 100 T2 T1 (a) 丢失修改 (b) 不可重复读 (c) 读“脏”数据 2019/11/20 数据库原理
8.2 封 锁(Locking) 什么是封锁 基本封锁类型 基本锁的相容矩阵 2019/11/20 数据库原理
封锁的概念 封锁就是事务T在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁。 加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象。 封锁是实现并发控制的一个非常重要的技术。 2019/11/20 数据库原理
基本封锁类型 DBMS通常提供了多种类型的封锁。一个事务对某个数据对象加锁后究竟拥有什么样的控制是由封锁的类型决定的。 基本封锁类型 排它锁(Exclusive lock,简记为X锁) 共享锁(Share lock,简记为S锁) 若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。 若事务T对数据对象A加上S锁,则其它事务只能再对A加S锁, 而不能加X锁,直到T释放A上的S锁。 2019/11/20 数据库原理
锁的相容矩阵 X S - N Y T1 T2 Y = Yes,相容的请求 N = No, 不相容的请求 2019/11/20 数据库原理
8.3 封锁协议 在运用X锁和S锁对数据对象加锁时,需要约定一些规则:封锁协议(Locking Protocol) 何时申请X锁或S锁 持锁时间、何时释放 不同的封锁协议,在不同的程度上为并发操作的 正确调度提供一定的保证 常用的封锁协议:三级封锁协议 2019/11/20 数据库原理
1级封锁协议 事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放 正常结束(COMMIT) 非正常结束(ROLLBACK) 1级封锁协议可防止丢失修改 在1级封锁协议中,如果是读数据,不需要加锁的,所以它不能保证可重复读和不读“脏”数据。 2019/11/20 数据库原理
没有丢失修改 读“脏”数据 T1 T2 T2 T1 ① Xlock A ① Xlock A 获得 获得 ② 读A=16 ② 读A=16 ③A←A-1 写回A=15 Commit Unlock A ④ ⑤ Xlock A 等待 获得Xlock A 读A=15 A←A-1 写回A=14 Unlock A 读A=15 ① Xlock A 获得 ② 读A=16 A←A-1 写回A=15 ③ ④ Rollback Unlock A T2 T1 没有丢失修改 读“脏”数据 2019/11/20 数据库原理
不可重复读 T2 T1 ①读A=50 求和=150 ② Xlock B 获得 读B=100 B←B*2 写回B=200 Commit Xlock B 获得 读B=100 B←B*2 写回B=200 Commit Unlock B ①读A=50 求和=150 ② ③读A=50 读B=200 求和=250 (验算不对) T2 T1 不可重复读 2019/11/20 数据库原理
2级封锁协议 1级封锁协议+事务T在读取数据R前必须先加S锁,读完后即可释放S锁 2级封锁协议可以防止丢失修改和读“脏”数据。 2019/11/20 数据库原理
不可重复读 T2 T1 T2 T1 (续) ① Sclock A 获得 读A=50 Unlock A ② Sclock B 读B=100 Unlock B ③ 求和=150 Xlock B 等待 获得Xlock B B←B*2 写回B=200 Commit Unlock B T2 T1 ④Sclock A 获得 读A=50 Unlock A Sclock B 读B=200 Unlock B 求和=250 (验算不对) T2 T1 (续) 不可重复读 2019/11/20 数据库原理
3级封锁协议 1级封锁协议 + 事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放 3级封锁协议可防止丢失修改、读脏数据和不可重复读。 2019/11/20 数据库原理
T1 T2 可 重 复 读 不读“脏”数据 ③ROLLBACK T1 T2 ① Xlock C 读C= 100 C←C*2 写回C=200 ① Slock A 读A=50 Slock B 读B=100 求和=150 ② ③ 读A=50 Commit Unlock A Unlock B ④ ⑤ Xlock B 等待 获得Xlock B B←B*2 写回B=200 T1 T2 ① Xlock C 读C= 100 C←C*2 写回C=200 ② ③ROLLBACK (C恢复为100) Unlock C ④ ⑤ Slock C 等待 获得Slock C 读C=100 Commit C 可 重 复 读 不读“脏”数据 2019/11/20 数据库原理
封锁协议小结 三级协议的主要区别 什么操作需要申请封锁 何时释放锁(即持锁时间) 2019/11/20 数据库原理
8.4 活锁和死锁 封锁技术可以有效地解决并行操作的一致性问题,但也带来一些新的问题 死 锁 活 锁 2019/11/20 数据库原理
活 锁 T2有可能永远等待,这就是活锁的情形。 2019/11/20 数据库原理
如何避免活锁 采用先来先服务的策略: 当多个事务请求封锁同一数据对象时 按请求封锁的先后次序对这些事务排队 该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁。 2019/11/20 数据库原理
死 锁 T1和T2两个事务永远不能结束,形成死锁。 2019/11/20 数据库原理
解决死锁的方法 预防死锁 死锁的诊断与解除 一次封锁法 顺序封锁法 超时法 等待图法 结论 操作系统中广为采用的预防死锁的策略并不很适合数据库 DBMS解决死锁更普遍采用的是诊断并解除死锁的方法 2019/11/20 数据库原理
一次封锁法 要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行。 一次封锁法存在的问题:降低并发度 将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度。 执行过程时很难事先精确地确定每个事务所要封锁地数据对象,须将执行过程中可能要封锁的对象全部加锁,从而进一步降低了并发度。 2019/11/20 数据库原理
顺序封锁法 顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。 顺序封锁法存在的问题 数据库系统中可封锁的数据对象极其众多,并且不断地变化,要维护这样的资源的封锁顺序非常困难,成本很高。 很难事先确定每个事务要封锁的对象,因此很难按规定的顺序去施加封锁。 例:规定数据对象的封锁顺序为A,B,C,D,E。事务T3起初要求封锁数据对象B,C,E,但当它封锁了B,C后,才发现还需要封锁A,这样就破坏了封锁顺序. 2019/11/20 数据库原理
超时法 如果一个事务的等待时间超过了规定的时限,就认为发生了死锁。 优点:实现简单 缺点 有可能误判死锁 时限若设置得太长,死锁发生后不能及时发现 2019/11/20 数据库原理
等待图法 用事务等待图动态反映所有事务的等待情况 事务等待图是一个有向图G=(T,U) T为结点的集合,每个结点表示正运行的事务 U为边的集合,每条边表示事务等待的情况 若T1等待T2,则T1,T2之间划一条有向边,从T1指向T2 并发控制子系统周期性地(比如每隔1 min)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁。 解除死锁 选择一个处理死锁代价最小的事务,将其撤消,释放此事务持有的所有的锁,使其它事务能继续运行下去。 2019/11/20 数据库原理
8.5 并发调度的可串行性 什么样的并发操作调度是正确的 如何保证并发操作的调度是正确的 2019/11/20 数据库原理
并发调度正确的判断 计算机系统对并行事务中并行操作的调度是的随机的,而不同的调度可能会产生不同的结果。 如果一个事务运行过程中没有其他事务在同时运行,也就是说它没有受到其他事务的干扰,那么就可以认为该事务的运行结果是正常的。 以不同的顺序串行执行事务有可能会产生不同的结果,但由于不会将数据库置于不一致状态,所以都认为是正确的。 几个事务的并行执行是正确的,当且仅当其结果与按某一次序串行地执行它们时的结果相同。这种并行调度策略称为可串行化(Serializable)的调度。 2019/11/20 数据库原理
事务1:读B;A=B+1;写回A;事务2:读A;B=A+1;写回B; 假设A的初值为2,B的初值为2。 可串行性是并行事务正确性的唯一准则 例:现在有两个事务,分别包含下列操作: 事务1:读B;A=B+1;写回A;事务2:读A;B=A+1;写回B; 假设A的初值为2,B的初值为2。 对这两个事务的不同调度策略 串行执行 串行调度策略1 串行调度策略2 交错执行 不可串行化的调度 可串行化的调度 2019/11/20 数据库原理
T1 T2 T1 T2 串行调度策略,正确的调度 Slock A X=A=3 Unlock A Xlock B B=X+1 写回B(=4) Slock A X=A=3 Unlock A Xlock B B=X+1 写回B(=4) Unlock B SlockA X=A=2 Unlock A Xlock B B=X+1 写回B(=3) Unlock B Slock B Y=B=2 Unlock B Xlock A A=Y+1 写回A(=3) Unlock A Slock B Y=B=3 Unlock B Xlock A A=Y+1 写回A(=4) Unlock A 串行调度策略,正确的调度 2019/11/20 数据库原理
T1 T2 T1 T2 不可串行化的调度 可串行化的调度 Slock A X=A=2 Unlock A Xlock B B=X+1 Slock A X=A=2 Unlock A Xlock B B=X+1 写回B(=3) Unlock B Slock A 等待 X=A=3 Unlock A Xlock B B=X+1 写回B(=4) Unlock B Slock B Y=B=2 Unlock B Xlock A A=Y+1 写回A(=3) Unlock A Slock B Y=B=2 Unlock B Xlock A A=Y+1 写回A(=3) Unlock A 不可串行化的调度 可串行化的调度 2019/11/20 数据库原理
并发调度正确的保证 保证并发操作调度正确性的方法 从理论上讲,在某一事务执行时禁止其他事务执行的调度策略一定是可串行化的调度,这也是最简单的调度策略,但这种方法实际上是不可行的,因为它使用户不能充分共享数据库资源。 保证并发操作调度正确性的方法 封锁方法:两段锁(Two-Phase Locking,简称2PL)协议 时标方法 乐观方法 2019/11/20 数据库原理
8.6 两段锁协议 两段锁协议(加锁和解锁)的内容 “两段”锁的含义 在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁。 在释放一个封锁之后,事务不再获得任何其他封锁。 “两段”锁的含义 事务分为两个阶段 第一阶段是获得封锁,也称为扩展阶段; 第二阶段是释放封锁,也称为收缩阶段。 2019/11/20 数据库原理
事务1遵守两段锁协议,而事务2不遵守两段协议。 例: 事务1的封锁序列: Slock A ... Slock B ... Xlock C ... Unlock B ... Unlock A ... Unlock C; 事务2的封锁序列: Slock A ... Unlock A ... Slock B ... Xlock C ... Unlock C ... Unlock B; 事务1遵守两段锁协议,而事务2不遵守两段协议。 2019/11/20 数据库原理
并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的。 所有遵守两段锁协议的事务,其并行执行的结果一定是正确的。 事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。 可串行化的调度中,不一定所有事务都必须符合两段锁协议。 2019/11/20 数据库原理
(b) 不遵守两段锁协议 (c) 不遵守两段锁协议 T1 Slock B 读B=2 Y=B Xlock A A=Y+1 写回A=3 Unlock B Unlock A T2 Slock A 等待 读A=3 X=A Unlock A Xlock B B=X+1 写回B=4 Unlock B T1 Slock B 读B=2 Y=B Unlock B Xlock A A=Y+1 写回A=3 Unlock A T2 Slock A 读A=2 X=A Unlock A Xlock B 等待 B=X+1 写回B=3 Unlock B T2 Slock A 等待 读A=3 Y=A Xlock B B=Y+1 写回B=4 Unlock B Unlock A T1 Slock B 读B=2 Y=B Unlock B Xlock A A=Y+1 写回A=3 Unlock A (a) 遵守两段锁协议 (b) 不遵守两段锁协议 (c) 不遵守两段锁协议 2019/11/20 数据库原理
一次封锁法要求每个事务必须一次将所有要使用的 数据全部加锁,否则就不能继续执行,因此一次封 锁法遵守两段锁协议。 两段锁协议与防止死锁的一次封锁法 一次封锁法要求每个事务必须一次将所有要使用的 数据全部加锁,否则就不能继续执行,因此一次封 锁法遵守两段锁协议。 但是两段锁协议并不要求事务必须一次将所有要使 用的数据全部加锁,因此遵守两段锁协议的事务可 能发生死锁。 2019/11/20 数据库原理
T1 T2 Slock B 读B=2 Slock A 读A=2 Xlock A 等待 Xlock B 等待 图 遵守两段锁协议的事务发生死锁 Xlock A 等待 T2 Slock A 读A=2 Xlock B 等待 图 遵守两段锁协议的事务发生死锁 2019/11/20 数据库原理
8.7 封锁的粒度 X锁和S锁都是加在某一个数据对象上的 封锁的对象:逻辑单元,物理单元 例:在关系数据库中,封锁对象: 逻辑单元: 属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等。 物理单元:页(数据页或索引页)、块等。 封锁对象可以很大也可以很小 例:对整个数据库加锁或对某个属性值加锁 2019/11/20 数据库原理
封锁对象的大小称为封锁的粒度(Granularity) 多粒度封锁(multiple granularity locking) 在一个系统中同时支持多种封锁粒度供不同的事务选择 封锁的粒度越 大 小 系统被封锁的对象 少 多 并发度 小 高 系统开销 小 大 选择封锁粒度: 考虑封锁机构和并发度两个因素 对系统开销与并发度进行权衡 2019/11/20 数据库原理
选择封锁粒度的原则 需要处理多个关系的大量元组的用户事务:以数据库为封锁单位; 需要处理大量元组的用户事务:以关系为封锁单元; 只处理少量元组的用户事务:以元组为封锁单位。 2019/11/20 数据库原理
8.7.1 多粒度封锁 多粒度树 以树形结构来表示多级封锁粒度 根结点是整个数据库,表示最大的数据粒度 叶结点表示最小的数据粒度 例:三级粒度树。根结点为数据库,数据库的子结点为关系,关系的子结点为元组。 2019/11/20 数据库原理
对一个结点加锁意味着这个结点的所有后裔结点也被加以同样类型的锁 在多粒度封锁中一个数据对象可能以两种方式封锁:显式封锁和隐式封锁 允许多粒度树中的每个结点被独立地加锁 对一个结点加锁意味着这个结点的所有后裔结点也被加以同样类型的锁 在多粒度封锁中一个数据对象可能以两种方式封锁:显式封锁和隐式封锁 显式封锁: 直接加到数据对象上的封锁 隐式封锁: 由于其上级结点加锁而使该数据对象加上了锁 显式封锁和隐式封锁的效果是一样的 2019/11/20 数据库原理
对某个数据对象加锁时系统检查的内容 该数据对象 所有上级结点 所有下级结点 有无显式封锁与之冲突 检查本事务的显式封锁是否与该数据对象上的隐式封锁冲突:(由上级结点封锁造成的) 所有下级结点 看上面的显式封锁是否与本事务的隐式封锁(将加到下级结点的封锁)冲突。 2019/11/20 数据库原理
8.7.2 意向锁 引进意向锁(intention lock)目的 提高对某个数据对象加锁时系统的检查效率 对任一结点加基本锁,必须先对它的上层结点加意向锁; 如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁。 例:对任一元组 r 加锁,先关系R加意向锁 事务T要对关系R加X锁, 系统只要检查根结点数据库和关系R是否已加了不相容的锁, 不需要搜索和检查R中的每一个元组是否加了X锁 2019/11/20 数据库原理
常用的意向锁 意向共享锁(Intent Share Lock,简称IS锁) 意向排它锁(Intent Exclusive Lock,简称IX锁) 共享意向排它锁(Share Intent Exclusive Lock, 简称SIX锁) 2019/11/20 数据库原理
IS 锁 如果对一个数据对象加IS锁,表示它的后裔结点 拟(意向)加S锁。 例:要对某个元组加S锁,则要首先对关系和数据库 加IS锁。 例:要对某个元组加S锁,则要首先对关系和数据库 加IS锁。 2019/11/20 数据库原理
IX 锁 如果对一个数据对象加IX锁,表示它的后裔结点 拟(意向)加X锁。 例:要对某个元组加X锁,则要首先对关系和数据库 加IX锁。 2019/11/20 数据库原理
SIX 锁 如果对一个数据对象加SIX锁,表示对它加S锁,再加IX锁,即SIX = S + IX。 2019/11/20 数据库原理
意向锁的相容矩阵 T1 T2 S X IS IX SIX - S Y N Y N N Y X N N N N N Y IS Y N Y Y Y Y IX N N Y Y N Y SIX N N Y N N Y - Y Y Y Y Y Y 2019/11/20 数据库原理
锁的强度 具有意向锁的多粒度封锁方法 锁的强度是指它对其他锁的排斥程度 一个事务在申请封锁时以强锁代替弱锁是安全的,反之则不然. 申请封锁时应该按自上而下的次序进行; 释放封锁时则应该按自下而上的次序进行。 例:事务T要对一个数据对象加锁,必须先对它的上层结点加意向锁。 2019/11/20 数据库原理
8.8 Oracle的并发控制 Oracle采用封锁技术保证并发操作的可串行性 Oracle锁的种类 字典锁(亦称DDL锁) :用于保护数据库对象的结构,如表、索引等的结构定义。 数据锁(亦称DML锁):用于保护数据的完整性。 在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。 2019/11/20 数据库原理
小 结 数据共享与数据一致性是一对矛盾。 数据库的价值在很大程度上取决于它所能提供的数据共享度。 数据共享在很大程度上取决于系统允许对数据并发操作的程度。 数据并发程度又取决于数据库中的并发控制机制。 另一方面,数据的一致性也取决于并发控制的程度。 2019/11/20 数据库原理
不同级别的封锁协议提供不同的数据一致性保证,提供不同的数据共享度。 数据库的并发控制以事务为单位 数据库的并发控制通常使用封锁机制 两类最常用的封锁 不同级别的封锁协议提供不同的数据一致性保证,提供不同的数据共享度。 三级封锁协议 并发控制机制调度并发事务操作是否正确的判别准则是可串行性 并发操作的正确性则通常由两段锁协议来保证。 两段锁协议是可串行化调度的充分条件,但不是必要条件。 2019/11/20 数据库原理
对数据对象施加封锁,带来问题 活锁: 先来先服务策略 死锁: 预防方法 一次封锁法 顺序封锁法 死锁的诊断与解除 超时法 等待图法 2019/11/20 数据库原理
不同的数据库管理系统提供的封锁类型、封锁协议、达到的系统一致性级别不尽相同。但是其依据的基本原理和技术是共同的。 2019/11/20 数据库原理