封锁是实现并发控制的重要技术,通过对数据对象加锁来限制其他事务对该对象的访问
封锁是实现并发控制的重要技术,通过对数据对象加锁来限制其他事务对该对象的访问。常见的封锁协议分为三级:
- 一级封锁协议:事务在修改数据R之前必须先对其加X锁(排他锁),直到事务结束才释放。一级封锁协议可以解决丢失修改问题,但无法保证可重复读和防止读脏数据。
- 二级封锁协议:在一级封锁协议的基础上,事务在读数据R之前必须先对其加S锁(共享锁),读完后即可释放S锁。二级封锁协议除了防止丢失修改外,还可以解决读脏数据问题,但无法保证可重复读。
- 三级封锁协议:在一级封锁协议的基础上,事务在读数据R之前必须先对其加S锁,直到事务结束时才释放S锁。三级封锁协议除了防止丢失修改和读脏数据外,还进一步防止了不可重复读问题。
2. 活锁与死锁
- 活锁:指当事务T₁封锁了数据R时,事务T₂请求封锁R,于是T₂等待;当T₁释放了R上的封锁后,系统首先批准了T₃请求,T₂仍等待;当T₃释放了R上的封锁后,又批准了T₄请求,依此类推,使得T₂可能永远等待的现象。活锁可以通过先来先服务的封锁请求调度策略避免。
- 死锁:指两个以上的事务分别请求封锁对方已经封锁的数据,导致长期等待而无法继续运行下去的现象。死锁的四个必要条件是:互斥条件、请求和保持条件、不可剥夺条件、循环等待条件。可以通过一次封锁法、顺序封锁法等策略预防死锁,也可通过超时法、等待图法检测死锁并解除。
3. 并发调度的可串行性
多个事务的并发执行是正确的,当且仅当其结果与某一次序串行执行它们时的结果相同,这种调度策略称为可串行化的调度。可串行性是并发事务正确性的准则,按这个准则规定,一个给定的并发调度,当且仅当它是可串行化的才认为是正确调度。
4. 两段封锁协议
所谓两段封锁协议,是指所有事务必须分两个阶段对数据项加锁和解锁:
- 第一阶段(获得封锁):事务可以获得任何数据项上的任何类型的锁,但不能释放。
- 第二阶段(释放封锁):事务可以释放任何数据项上的任何类型的锁,但不能申请。
两段锁协议是保证并发调度可串行性的充分条件,遵循两段锁协议的事务调度一定是可串行化的。
5. 封锁的粒度
封锁对象的大小称为封锁的粒度。封锁的对象可以是逻辑单元(如属性、元组、关系、索引项、整个索引甚至整个数据库),也可以是物理单元(如数据页或索引页)。封锁粒度与系统的并发度和开销密切相关:粒度越小,并发度越高,但锁管理开销也越大;粒度越大,并发度越低,锁管理开销越小。
二、考试试题
(一)单项选择题(每题2分,共20分)
- 在数据库系统中,解决并发操作带来的数据不一致问题普遍采用( )技术。
A. 封锁
B. 存取控制
C. 恢复
D. 协商 - 一级封锁协议可以解决以下哪种并发问题( )。
A. 丢失修改
B. 不可重复读
C. 读脏数据
D. 死锁 - 二级封锁协议在一级封锁协议的基础上增加了( )。
A. 事务在修改数据R之前必须先对其加X锁,直到事务结束才释放
B. 事务在读数据R之前必须先对其加S锁,读完后即可释放S锁
C. 事务在读数据R之前必须先对其加S锁,直到事务结束才释放S锁
D. 事务在读数据R之前必须先对其加X锁,读完后即可释放X锁 - 以下哪种封锁协议可以同时解决丢失修改、读脏数据和不可重复读问题( )。
A. 一级封锁协议
B. 二级封锁协议
C. 三级封锁协议
D. 两段封锁协议 - 关于两段封锁协议,以下说法错误的是( )。
A. 事务分为两个阶段:获得封锁阶段和释放封锁阶段
B. 获得封锁阶段只能申请锁,不能释放锁
C. 释放封锁阶段只能释放锁,不能申请锁
D. 遵循两段封锁协议的调度一定是正确的,不会发生死锁 - 以下哪种情况会导致活锁( )。
A. 事务T1持有锁L1,请求锁L2;事务T2持有锁L2,请求锁L1
B. 事务T1封锁了数据R,事务T2请求封锁R,T1释放R后系统总是优先批准其他事务的封锁请求,导致T2长期等待
C. 两个事务同时读取同一数据项
D. 两个事务同时修改同一数据项 - 可串行化调度是指( )。
A. 多个事务串行执行的调度
B. 多个事务并发执行的结果与某一次序串行执行的结果相同
C. 多个事务按照时间顺序依次执行
D. 多个事务不会发生冲突的调度 - 如果事务T获得了数据项Q上的排他锁(X锁),则T对Q( )。
A. 只能读不能写
B. 只能写不能读
C. 既可读又可写
D. 不能读也不能写 - 以下哪种操作不会发生冲突( )。
A. T1正在写A,T2要读A
B. T1正在写A,T2也要写A
C. T1正在读A,T2要写A
D. T1正在读A,T2也要读A - 死锁的四个必要条件中,不包括( )。
A. 互斥条件
B. 可剥夺条件
C. 请求和保持条件
D. 循环等待条件
(二)多项选择题(每题3分,共15分)
- 并发操作可能带来的数据不一致性问题包括( )。
A. 丢失修改
B. 不可重复读
C. 读脏数据
D. 死锁
E. 活锁 - 常见的封锁类型包括( )。
A. 共享锁(S锁)
B. 排他锁(X锁)
C. 读写锁
D. 意向锁
E. 时间戳 - 以下关于封锁粒度的说法正确的是( )。
A. 封锁粒度越大,系统的并发度越高
B. 封锁粒度越小,系统的并发度越高
C. 封锁粒度越大,锁管理的开销越小
D. 封锁粒度越小,锁管理的开销越小
E. 选择封锁粒度时需要在并发度和锁开销之间进行权衡 - 死锁的预防策略包括( )。
A. 一次封锁法
B. 顺序封锁法
C. 先来先服务
D. 超时法
E. 等待图法 - 以下关于两段封锁协议的说法正确的是( )。
A. 两段封锁协议是保证调度可串行性的充分条件
B. 遵循两段封锁协议的调度一定是可串行化的
C. 遵循两段封锁协议的调度不会发生死锁
D. 两段封锁协议要求所有事务必须分两个阶段对数据项加锁和解锁
E. 两段封锁协议中,事务在释放锁之后不能再申请任何锁
(三)判断题(每题2分,共10分)
- 一级封锁协议可以防止读脏数据问题。( )
- 二级封锁协议可以保证可重复读。( )
- 三级封锁协议要求事务在读数据前加S锁,直到事务结束才释放。( )
- 可串行性是并发事务正确性的判断准则。( )
- 两段封锁协议可以避免死锁的发生。( )
(四)简答题(每题5分,共20分)
- 简述三级封锁协议的内容以及各自能解决的并发问题。
- 解释活锁和死锁的概念,并说明如何预防和解决。
- 什么是可串行化调度?为什么可串行化调度是正确的?
- 简述两段封锁协议的内容及其作用。
(五)综合题(每题15分,共15分)
设有两个事务T1和T2,其操作序列如下:
T1:读A;A = A - 100;写回A;读B;B = B + 100;写回B;
T2:读B;B = B - 100;写回B;读A;A = A + 100;写回A;
假设A和B的初始值都为1000。
- 如果这两个事务串行执行(先执行T1再执行T2,或先执行T2再执行T1),请分别计算执行后的A和B的值。
- 现有一个并发调度如下:
T1读A → T2读B → T1写回A → T2写回B → T1读B → T2读A → T1写回B → T2写回A
请计算该调度执行后的A和B的值,并判断该调度是否是可串行化调度,说明理由。 - 如果使用三级封锁协议,请描述上述并发调度的执行过程,并说明最终结果是否正确。
三、试题答案
(一)单项选择题答案
- A
- A
- B
- C
- D
- B
- B
- C
- D
- B
(二)多项选择题答案
- ABC
- AB
- BCE
- AB
- ABDE
(三)判断题答案
- × 解析:一级封锁协议只能解决丢失修改问题,无法防止读脏数据。
- × 解析:二级封锁协议读完数据后即可释放S锁,无法保证可重复读。
- √
- √
- × 解析:两段封锁协议不能避免死锁,反而可能增加死锁发生的概率。
(四)简答题答案
- 三级封锁协议内容及解决的问题:
- 一级封锁协议:事务在修改数据R之前必须先对其加X锁,直到事务结束才释放。可以解决丢失修改问题。
- 二级封锁协议:在一级封锁协议基础上,事务在读数据R之前必须先对其加S锁,读完后即可释放S锁。可以解决丢失修改和读脏数据问题。
- 三级封锁协议:在一级封锁协议基础上,事务在读数据R之前必须先对其加S锁,直到事务结束才释放S锁。可以解决丢失修改、读脏数据和不可重复读问题。
- 活锁与死锁:
- 活锁:指事务请求封锁数据时,由于其他事务不断获得该数据的封锁,导致该事务长期等待的现象。活锁可以通过先来先服务的调度策略避免。
- 死锁:指两个或多个事务互相等待对方持有的封锁,导致都无法继续执行的现象。死锁的预防策略包括一次封锁法(事务一次性申请所有需要的锁)和顺序封锁法(规定所有事务按照相同顺序申请锁);死锁的检测方法包括超时法和等待图法,检测到死锁后可以通过撤销代价最小的事务来解除死锁。
- 可串行化调度:
可串行化调度是指多个事务并发执行的结果,与某一次序串行执行这些事务的结果相同。由于串行执行的事务不会互相干扰,其结果一定是正确的,因此如果并发调度的结果与某一串行执行结果相同,那么该调度就是正确的。 - 两段封锁协议:
两段封锁协议要求所有事务必须分两个阶段对数据项加锁和解锁:第一阶段是获得封锁阶段,事务可以申请任何数据项上的任何类型的锁,但不能释放任何锁;第二阶段是释放封锁阶段,事务可以释放任何数据项上的任何类型的锁,但不能再申请任何锁。两段封锁协议是保证并发调度可串行性的充分条件,遵循两段封锁协议的调度一定是可串行化的。
(五)综合题答案
- 串行执行结果:
- 先执行T1再执行T2:
T1执行后:A = 900,B = 1100
T2执行后:B = 1000,A = 1000
最终结果:A=1000,B=1000 - 先执行T2再执行T1:
T2执行后:B = 900,A = 1100
T1执行后:A = 1000,B = 1000
最终结果:A=1000,B=1000
- 先执行T1再执行T2:
- 并发调度结果:
执行过程:
T1读A=1000 → T2读B=1000 → T1写回A=900 → T2写回B=900 → T1读B=900 → T2读A=900 → T1写回B=1000 → T2写回A=1000
最终结果:A=1000,B=1000
该调度是可串行化调度,因为其结果与两种串行执行的结果都相同。 - 三级封锁协议下的执行过程:
- T1开始,先申请A的X锁,获得后读A=1000,修改为900并写回。
- T2开始,申请B的X锁,获得后读B=1000,修改为900并写回。
- T1申请B的X锁,此时B已被T2加了X锁,T1等待。
- T2申请A的X锁,此时A已被T1加了X锁,T2等待。
- 此时发生死锁,两个事务都无法继续执行。系统需要检测到死锁并撤销其中一个事务,释放其持有的锁,另一个事务才能继续执行,最终结果会与串行执行结果一致,是正确的。
