上篇博客通过一些实例介绍了并发导致的数据不一致一系列问题——丢失修改、读脏数据、不可重复读
本篇博客介绍如何进行并发控制。
——封锁。
1,封锁
(1)封锁就是事务T在对某数据对象(例如:表、记录)操作之前,先向系统发出请求,对其加锁
(2)加锁后事务T就对数据对象有了一定的控制,在事务T释放它的锁之前,其他的事务不能更新此数据对象
2,封锁类型
不同的锁有不同的控制功能;即,一个事务对数据对象加锁后可有怎样的控制由它的封锁类型决定。
(1)排他锁(简称X锁,或写锁)
a.含义:若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。
b.功能:保证其他事务在事务T释放数据对象A上的锁之前不能在读和修改A。
(2)共享锁(简称S锁,或读锁)
a.含义:若事务T对数据对象A加上S锁,则其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁
b.功能:保证其他事务可以读数据对象A,但在事务T释放A上的S锁之前不能对A做任何修改、
关注点在哪里,一级封锁协议的关注点是事务修改的数据,二级和三级是事务读取的数据
3,三级封锁协议及分别解决的问题
(1)一级:
a.内容:事务T在修改数据对象A之前必须对其加X锁,直到事务结束才释放。
b.可解决的问题:“丢失修改”问题
T1 | T2 | 解释 |
① Xlock A |
| 事务T1在读A进行修改之前先对A加X锁 |
② R(A)=16 |
|
|
| Xlock A | 当T2再请求对A加X锁时被拒绝 |
③ A←A-1 | 等待 |
T2只能等待T1释放A上的锁后T2获得对A的X锁
|
W(A)=15 | 等待 |
|
Commit | 等待 |
|
Unlock A | 等待 |
|
④ | 获得Xlock A |
|
| R(A)=15 | 这时T2读到的A已经是T1更新过的值15 |
| A←A-1 |
T2按此新的A值进行运算,并将结果值A=14送回到磁盘。避免了丢失T1的更新。 |
⑤ | W(A)=14 |
|
| Commit |
|
| Unlock A |
|
思考:一级封锁协议为什么不能解决脏读和不可重复读问题?
在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读“脏”数据。即,事务1对R加了排他锁,对数据R进行了修改,若事事务2对R申请锁,则会wait;若事务2不对R申请任何锁,则即使事务1对R加了排他锁,事务2也会无视,可以读!所以不能解决丢失修改和不可重复读问题
(2)二级:
a.内容:在一级封锁协议的基础上,另外加上事务T在读取数据对象A之前必须对其加S锁,读完后立即释放。
b.可以解决的问题:“丢失修改”、“读脏数据”问题
T1 | T2 | 解释 |
① Xlock C |
| 事务T1在对C进行修改之前,先对C加X锁,修改其值后写回磁盘
|
R(C)=100 |
|
|
C←C*2 |
|
|
W(C)=200 |
|
|
② | Slock C |
T2请求在C上加S锁,因T1已在C上加了X锁,T2只能等待
|
| 等待 |
|
③ROLLBACK | 等待 |
|
(C恢复为100) | 等待 | T1因某种原因被撤销,C恢复为原值100
|
Unlock C | 等待 |
|
④ | 获得Slock C |
|
| R(C)=100 | T1释放C上的X锁后T2获得C上的S锁,读C=100。避免了T2读“脏”数据
|
⑤ | Commit C |
|
| Unlock C |
|
思考:为什么二级封锁不能解决“不可重复读”问题?
——事务1读取数据时加上共享锁后(这样在事务1读取数据的过程中,其他事务就不会修改该数据),
不允许任何事物操作该数据,只能读取,之后1如果有更新操作,那么会转换为排他锁,其他事务更无权参与进来读写,
这样就防止了脏读问题。但是当事务1读取数据过程中,有可能其他事务(例如事务2)也读取了该数据,事务1读取完毕后共享锁释放(此时事务1还未完),
此时事务2修改数据,修改完毕提交事务,那么事务1再次读取数据时候发现数据不一致,就会出现不可重复读问题,
所以这样不能够避免不可重复读问题。
(3)三级:
a.内容:在一级封锁协议的基础上,另外加上事务T在读取数据对象A之前必须对其加S锁,直到事务结束才释放。
b.可以解决的问题:“丢失修改”、“读脏数据”、“不可重复读”问题
T1 | T2 | 解释 |
|
| 事务T1在读A,B之前,先对A,B加S锁; 其他事务只能再对A,B加S锁,而不能加X锁,即其他事务只能读A,B,而不能修改
|
Slock B |
|
|
R(A)=50 |
|
|
R(B)=100 |
|
|
求和=150 |
|
|
② | Xlock B |
当T2为修改B而申请对B的X锁时被拒绝只能等待T1释放B上的锁
T1为验算再读A,B,这时读出的B仍是100,求和结果仍为150,即可重复读
T1结束才释放A,B上的S锁。T2才获得对B的X锁 |
| 等待 |
|
| 等待 |
|
③ R(A)=50 | 等待 |
|
R(B)=100 | 等待 |
|
求和=150 | 等待 |
|
Commit | 等待 |
|
Unlock A | 等待 |
|
Unlock B | 等待 |
|
④ | 获得XlockB |
|
| R(B)=100 |
|
| B←B*2 |
|
⑤ | W(B)=200 |
|
| Commit |
|
| Unlock B |
|
注:关注点在哪里?
—— 一级封锁协议的关注点是事务修改的数据,二级和三级是事务读取的数据
4,总结:
(1)锁的相容矩阵:
(如果R上加了X锁后,就不能再加任何锁!如果R上加了S锁后,只能再加S锁而不能再加X锁!)
(2)
| X锁 | S锁 |
| 无丢失修改 | 无污读 | 可更新 |
| 事务结束释放 | 事务结束释放 | 读完释放 |
|
|
|
一级封锁协议 | √ |
|
| √ |
|
|
二级封锁协议 | √ |
| √ | √ | √ |
|
三级封锁协议 | √ | √ |
| √ | √ | √ |