一:引言
事物可以一个一个地串行执行,即每个时刻只有一个事物运行,其他事物必须等到这个事物结束以后才能运行。
交叉并发方式:在单处理机系统中,事物的并行执行实际上是这些并行事物的并行操作轮流交叉运行。
同时并发方式:在多处理机系统中,每个处理机可以运行一个事物,多个处理机可以同时运行多个事物,实现多个事物真正的并行运行。
如果多个用户并发地存取数据库时就会产生多个事物同时存取同一个数据的情况。
若对并发操作不加控制就可能会存取和存储不正确的数据,破坏事物的一致性和数据库的一致性。
二:并发控制概述
并发操作破坏事物的隔离性。并发控制就是要正确的方式调度并发操作,是一个用户事物的执行不受其他事物的干扰,从而避免造成数据的不一致。
1: 数据丢失
两个事物T1和T2读入统一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失。
2: 不可重复读
事物T1读取数据后,事物T2执行更新操作,使T1无法再现前一次读取结果。
A: 事物T1读取某一数据后,事物T2对其做修改,当事物T1再次读该数据时,得到与前一次不同的值。
B: 事物T1按一定的条件从数据库中读取了某些数据记录后,事物T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现某些记录神秘的消失了。
C: 事物T1按一定的条件从数据库中读取了某些数据记录后,事物T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。
3: 读”脏“数据
事物T1修改某一数据后,并将其写回磁盘,事物T2读取统一数据后,T1由于某种原因被撤销,这是T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,则T2读到的数据就为”脏“数据,即不正确的数据。
三:封锁
封锁是实现并发控制的一个非常重要的技术。即事物T在对某个数据对象操作前,先向系统发出请求,对其加锁。
加锁后事物T就对该事物数据对象有了一定的控制,在事物T释放它的锁之前,其他事物不能更新此数据对象。
1: 排它锁(写锁),又称X锁
若事物T对数据库对象A加上X锁,则只允许T读取和修改A,其他任何事物都不能对A加任何类型的锁,知道T释放A上的锁。
这就保证了其他事物在T释放A上的锁之前不能再读取和修改A。
2: 共享锁(读锁),又称S锁
若事物T对数据对象A加上S锁,则事物T可以读A但不能修改A,其他事物只能再随A加S锁,而不能加X锁,知道T释放A上的S锁。
这就保证了其他事物可以读A,但在T释放A上的S锁之前不能对A做任何修改。
四:活锁和死锁
1: 活锁
如果事物T1封锁了数据R,事物T2又请求封锁数据R,于是T2等待。T3也请求封锁数据R。
当T1释放了数据R上的封锁之后系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁数据R,
当T3释放了数据R上的封锁之后系统又批准了T4的请求...,T2有可能永远等待。即活锁
2: 活锁的解决办法
简单方法是采用先来先服务的策略。当多个事物请求封锁统一数据对象时,封锁子系统按请求封锁的先后次序对事物排队,数据对象上的锁一旦释放就批准申请队列中第一个事物获得锁。
3: 死锁
如果事物T1封锁了数据R1,事物T2封锁了数据R2,
然后T1又请求封锁数据R2,因T2已封锁了R2,于是T1等待T2释放R2上的锁。
接着T2又申请封锁R1,因T1已封锁了R1,T2也只能等待T1释放R1上的锁。
这样就出现了T1在等待T2,而T2又在等待T1的局面,T1和T2两个事物永远不能结束,形成死锁。
4: 死锁的预防
防止死锁的发生其实就是要破坏产生死锁的条件。
A: 一次封锁法:要求每个事物必须一次将所有要使用的数据全部加锁,否则就不能继续执行。
一次就将所有要用到的全部加锁,势必扩大封锁范围,从而降低了系统的并发度。
数据库中数据库不断变化,原来不要求封锁的数据在执行过程中可能会变成封锁对象。
B: 顺序封锁法:预先对数据规定一个封锁顺序,所有事物都按这个顺序实行封锁。
数据库系统中封锁的数据对象较多,并且随数据的插入删除等操作而不断变化,要维护这样的资源的封锁顺序非常难,成本很高。
事物的封锁请求可以随着事物的执行而动态决定,很难实现确定每一个事物要封锁哪些对象,因此也很难按照规定的顺序去施加封锁。
5: 死锁的诊断并解除
A: 超时法:如果一个事物的等待时间超过了规定的时限,就认为发生了死锁。
有可能误判死锁,事物因为其他原因使等待时间超多时限,系统会误认为发生了死锁。
时限若设置得太长,死锁发生后不能及时发现。
B: 等待图法:动态反应所有事物的等待情况。
并发子系统周期性地生成事物等待图,并进行检测。如果发现图中存在回路,则表示系统中出现了死锁。
C: 解除死锁的方法通常是选择一个处理死锁代价最小的事物,将其撤销,释放此事物持有的所有锁,使其他事物得以继续运行下去。
当然,对撤销的事物所执行的数据修改操作必须加以恢复。
五:并发调度的可行性
1: 可串行化调度:多个事物的并发执行是正确的,当且仅当其结果与按某一次序串行执行这些事物时的结果相同。
可串行性:是并发事物正确调度的准则,按照这个准则规定,一个给定的并发调度,当且仅当它是可串行化的,才认为是正确调度。
2: 冲突的可串行化调度。
冲突操作是指不同的事物对同一个数据的读写操作和写写操作。
六:两段锁协议
为了保证并发调度的正确性,DBMS的并发控制机制必须提供一定的手段来保证调度是可串行化的。
1: 封锁协议
在运行封锁方法时,对数据对象加锁时需要约定一些规则,如何时申请封锁,持锁时间,何时释放锁等。
2: 两段锁协议:指所有事物必须分两个阶段对数据项加锁和解锁
A: 在对任何数据进行读写操作之前,首先要申请并获得对该数据的封锁
在这阶段,事物可以申请获得任何数据项上的任何类型锁,但是不能释放任何锁。
B: 在释放一个封锁之后,事物不再申请和获得任何其他封锁。
在这阶段,事物可以释放任何数据项上的任何类型锁,但是不能再申请任何锁。
七:封锁的粒度
封锁力度越大,数据库所能封锁的数据单元就越少,并大度就越小,系统开销也越小。
封锁力度越小,并大度较高,但系统开销也就越大。
1: 多粒度封锁
如果在一个系统中同时支持多种封锁粒度工不同的事物选择是比较理想的。
2: 多粒度树
多粒度树的根节点是整个数据库,表示最大的数据粒度,叶节点表示最小的数据粒度。
3: 多粒度封锁协议
允许多粒度树种的每个节点被独立地加锁。对一个节点加锁意味着这个节点的所有后裔节点也被加以同样类型的锁。
显式封锁:应事物要求直接加大数据对象上的封锁。
隐式加锁:该数据对象没有独立加锁,是由于其上级节点加锁而使该数据对象加上锁。
4: 意向锁:如果对一个节点加意向锁,则说明该节点的下层节点正在被加锁。对任何一个节点加锁,必须先对它的上层节点加意向锁。
IS锁:如果对一个数据对象加IS锁,表示它的后裔节点加S锁。
IX锁:如果对一个数据对象加IX锁,表示它的后裔节点加X锁。
SIX锁:如果对一个数据对象加SIX锁,表示对它加S锁,再加IX锁:SIX=S+IX;
具有意向锁的多粒度封锁方法中任意事物T要对一个数据对象加锁,必须先对它的上层节点加意向锁。