首先区分交叉并发方式和同时并发方式。
8.1,并发控制概述
并发带来的三种数据不一致性:1,丢失修改;2,不可重复读(又可分三种具体情况,后两种称为幻影现象);3,读脏数据。
8.2,封锁
两种锁:排它锁(X锁)和共享锁(S锁)。
排它锁称为写锁,即只有加锁的事务可以对其读取和修改,其他事务不能对其加任何锁,即既不能读也不能写;共享锁称为读锁,当某事务对数据加S锁,则其他事物可以对其加S锁,一切共享读数据。
8.3,封锁协议
一级封锁协议
当事务T对数据R修改之前必须对其加X锁,直到操作完毕。
一级封锁协议中,若只读数据不写数据是不需要加锁的,所以不能解决不可重复读和脏数据。
二级封锁协议
在一级协议之上,加上当事务T读取数据R时,必须加上S锁,读取后释放S锁。
这能解决脏数据问题,但是不能解决可重复读。
三级封锁协议
在一级协议之上,加上当事务T读取数据R时,必须加上S锁,事务结束后才能释放。
这就可以重复读了。
8.4,活锁和死锁
活锁
本来可以得到锁,但是被插队了,导致一直得不到锁。通过先来先服务的原则可以解决活锁问题。
死锁
死锁产生的原因
事务T1首先拿到了A锁,事务T2首先拿到了B锁,T1过了一会需要B锁,于是等待T2释放B锁,但是T2过了一会需要A锁,于是等待T1释放A锁,这就导致两方永无止境的等待下去。
通过两种方法来解决死锁问题,死锁预防和死锁检测解除。
死锁预防
一次封锁法:事务必须一开始就取得全部需要的锁。这会导致并发度降低。
顺序封锁法:建立B树来确定正确的拿锁顺序,但是很困难,成本很高。
死锁检测解除
超时法:如果事务运行超过预期时间,则可认为死锁,上去解除。问题是有可能误判,同时如果预期时间设置过长,会导致不能及时发现。
等待图法:建立有向图,节点为事务,边为等待情况。周期性检测是否有回路,有则说明死锁。
8.5,并发调度的可串行性
多个事务的并发执行是正确的,当且仅当其结果与按某一次序顺序执行的结果相同时,则称为可串行化。
一个给定的并发调度,当且仅当是可串行化时,才是正确调度。
通过二段所来保证事务的正确调度。
8.6,二段锁协议
事务对锁的操作分为两个阶段:扩展阶段(拿锁阶段)和收缩阶段(放锁阶段)。
扩展阶段只能拿锁不能放锁,收缩阶段只能放锁不能拿锁。
二段锁协议保证可串行化,但是可串行化不一定都是二段锁协议。
二段锁协议和一次封锁法有区别,一次封锁法是在开头拿所有锁,而二段锁协议是在一个阶段渐进的拿锁,这意味着二段锁协议可能导致死锁。
8.7,封锁的粒度
就是封锁对象的大小,是元组,还是数据项,还是整个表或者更大的。
粒度越大,并发度越低,系统开销越小;粒度越小,并发度越高,开销越大。
多粒度封锁
为了灵活处理,系统如果支持不同粒度供事务选择,则成为多粒度封锁。
根据层次关系建立粒度树,在粒度树种某一节点加的锁称为显示封锁,该锁会扩散到其所有子孙节点,称为隐式封锁。
显然这种显隐式封锁在每次加锁前要增加许多检查,故效率较低。
意向锁
在对任一节点加锁时,必须先对其上层节点加意向锁。
三种意向锁:意向共享锁(IS锁)、意向排它锁(IX锁)、共享意向排它锁(SIX锁)。
IS锁:某节点加IS锁,则其后裔“意向”加S锁,这里意向可以理解为打算。
IX锁,某节点加IX锁,则其后裔“意向”加X锁。
SIX锁:某节点加SIX锁,则该节点加S锁,然后再加IX锁,则其后裔意向加X锁。