首页 百科知识 事务隔离级别如何保证并发安全

事务隔离级别如何保证并发安全

时间:2022-10-17 百科知识 版权反馈
【摘要】:事务并发执行是数据共享的重要保证,但是事务并发又可能带来数据不一致的现象,破坏数据库的完整性。数据共享是数据库的基本优势之一,多用户同时使用数据库是数据库共享的主要体现。这种不一致或者不存在的数据通常称为“脏”数据。产生上述三类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制的主要技术是封锁。

1.4.3 数据库的并发控制

事务并发执行是数据共享的重要保证,但是事务并发又可能带来数据不一致的现象,破坏数据库的完整性。为了在并发执行过程中保持完整性,需要研究并发控制技术。

一、事务的并发执行

事务活动中,只有当一个事务完全结束,另一个事务才开始执行,这种执行方式称为事务的串行或者串行访问。在事务执行过程中,如果DBMS 同时接纳多个事务,使得事务在时间上可以重叠执行,这种执行事务方式称为事务的并发执行或者并发访问。根据计算机系统的不同,并发执行又可分为两种类型:

■ 在单CPU系统中,同一时间只能有一个事务占用CPU,实际情形是各个并发执行的事务交叉使用CPU,这种称为交叉或分时并发。

■ 在多CPU系统中,多个并发执行的事务可以同时占用系统中的CPU,这种称为同时并发。

二、并发操作引发的问题

数据共享是数据库的基本优势之一,多用户同时使用数据库是数据库共享的主要体现。在同一时刻,多个用户存取数据,由于使用时间的相互重叠和使用方式的相互影响,并发操作容易带来这三类数据不一致性问题:丢失修改、读“脏”数据、不可重复读取。

(1)丢失修改

丢失(Lost Update)是指两个事务T1和T2从数据库读取同一数据并进行修改,其中事务T2提交的修改结果破坏了事务T1提交的修改结果,导致了事务T1的修改被丢失。丢失修改是由于两个事务对同一数据并发地进行写入操作所引起的,因而称为写-写冲突(Write-write Conflict)。

(2)读“脏”数据

读“脏”数据(Dirty Read)是指事务T1将数据a修改成b,然后将其写入磁盘;此后事务T2读取该修改后的数据,即数据b;接下来T1因故被撤销,使得数据b恢复得到了原值a。这时,T2得到的数据就与数据库内的数据不一致。这种不一致或者不存在的数据通常称为“脏”数据。读取“脏”数据是由于一个事务读取另一个事务尚未提交的数据所引起的,因而称之为读-写冲突(Read-write Conflict)。

(3)不可重复读取

不可重复读(Non-repeatable Read)是指当事务T1读取数据a后,事务T2进行读取并进行更新操作,使得T1再读取a进行校验时,发现前后两次读取值发生了变化,从而无法再读取前一次读取的结果。

产生上述三类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据不一致性。并发控制的主要技术是封锁。

三、封锁

封锁是实现并发控制的一个非常重要的技术,所谓封锁就是某一事务对某一数据进行操作之前先对其加锁,加锁后该事务对此数据有了全部控制权,在该事务释放此锁之前其他任何事务都不能再对此数据进行更新操作。

基本的封锁类型有两种:排它锁(Exclusive Locks,简称X锁)和共享锁(Share Locks,简称S锁)

(1)排它锁

排它锁又称为写锁。若事务T对数据对象A加上X锁,则只允许T读取和修改A,而其他任何事务都只能等到T解除X锁之后,才能对A进行封锁和操作。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。

(2)共享锁

共享锁又称读锁。事务T对数据A加S锁后,T可以读A但不能写A;同时其他事务可以对A加S锁而不能加X锁。这就保证了其他事务可以读取A,但在T释放A上的S锁之前不能对A做任何修改。

四、封锁协议

在运行X锁和S锁这两种基本封锁,对数据加锁时,还需要约定一些规则,例如何时申请X锁或S锁、持锁时间、何时释放等,称这些规则为封锁协议(Locking Protocol)。对封锁方式规定不同的规则,就形成了各种不同封锁协议。下面介绍三级封锁协议:

■ 一级封锁协议:事务T在修改数据R之前必须先对其加X锁,直到事务结束释放。一级封锁协议可防止丢失修改,并保证事务T是可恢复的。

■ 二级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。二级封锁协议防止了丢失修改和读“脏”数据。

■ 三级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束释放。三级封锁协议防止了丢失修改和读“脏”数据以及不可重复读。

上述3种协议的主要区别在于针对不同操作需要申请封锁以及何时释放(持锁时间)。

五、活锁和死锁

封锁的方法可能引起活锁和死锁。

(1)活锁

活锁就是指在封锁过程中,系统可能使某个事务永远处于等待状态,得不到封锁机会。

如果事务T1封锁数据R,事务T2又请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求……T2有可能永远等待,这就是活锁的情形。

避免活锁的简单办法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对事务排队,数据对象上的锁一旦释放就批准队列中的第一个事务获得锁。

(2)死锁

死锁就是很多事务都处于等待状态,相互等待对方解除封锁,最后造成所有的事务都无法进行。

如果事务T1封锁数据R1,T2封锁了数据R2,然后T1又请求封锁R2,因T2已经封锁了R2,于是T1等待T2释放R2上的锁。接着T2又申请封锁R1,因T1已封锁了R1,T2也只有等待T1释放R1上的锁。这样就出现了T1在等待T2,而T2又在等待T1的局面,T1和T2两个事务永远不能结束,形成死锁。

在数据库中解决死锁问题主要有两类方法:一类方法是采取一定措施来预防死锁的发生;另一类方法是允许发生死锁,采用一定手段定期检测系统中有无死锁,若有则解除。

(3)两段锁协议

两段锁协议是指所有事务必须分两个阶段对数据项加锁和解锁。

在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁;在释放一个封锁之后,事务不再申请和获得任何其他封锁。

“两段”锁的含义是,事务分为两个阶段,第一阶段是获取封锁,也称为扩展阶段。在这一阶段,事务可以申请获得任何数据项上的任何类型的锁,但是不能释放任何锁。第二阶段是释放封锁,也称为收缩阶段。在这一阶段,事务可释放任何数据项上的任何类型的锁,但是不能再申请任何锁。

事务遵守两段锁协议是可串行调度的充分条件,而不是必要条件。也就是说,若并发事务都遵守两段锁协议,则对这些事务的任何并发调度策略都是可串行化的;若对并发事务的一个调度是可串行化的,不一定所有事务都符合两段锁协议。

另外,两段锁协议和防止死锁的一次封锁是不同的。一次封锁要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行,因此一次封锁法遵守两段锁协议;但是两段锁协议并不要求事务必须一次将所有要使用的数据全部加锁,因此遵守两段锁协议的事务可能发生死锁。

六、封锁的粒度

封锁对象的大小称为封锁粒度(Granularity)。封锁对象可以是逻辑单元,也可以是物理单元。在关系数据库中封锁粒度一般有这几种:属性(值)、属性(值)的集合、元组、关系表、物理页面、索引、关系数据库。

封锁粒度与系统的并发度和并发控制的开销密切相关。封锁粒度越大,数据库并发度就小但开销小,封锁粒度越小,数据库并发度大但开销大。

选择封锁粒度应该同时考虑封锁开销和并发度两个因素。一般来说,需要处理大量元组的事务可以以关系为封锁粒度;需要处理多个关系的大量元组的事务可以以数据库为封锁粒度,而对于处理少量元组的用户事务,以元组为封锁粒度就较合适。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈