Melinets图书管理系统死锁问题研究
2009-07-16王德山
王德山
[摘要]不同的数据库管理系统提供的封锁类型、封锁协议、达到的系统一致性级别不尽相同,但其依据的基本原理和技术是共同的。通过对melinets图书管理系统死锁问题的研究,分析了SYBASE数据库锁的机制,提出了预防死锁的几种措施,并给出了解除死锁的相关方法。
[关键词]melinets sybase 死锁
中图分类号:TP3文献标识码:A文章编号:1671-7597(2009)0420041-02
Melinets是北京邮电大学开发的一种集采访、编目、流通、期刊等子系统在内的图书馆自动化管理系统,其后台采用SYBASE数据库。它通过锁的方式保护被活动的事务正在使用的表、数据页或者数据行。加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。确切的控制由锁的类型来决定。原郑州经济管理干部学院(现河南工程学院)于2003年至2008年10月一直使用该图书管理系统,在对图书进行数据加工,书目数据发送等过程中,经常出现进程阻塞甚至死锁的现象。本文从SYBASE数据库死锁的原理出发,结合melinets死锁发生的具体情况,提出了在应用melinets过程中预防和处理死锁的相关措施。
一、Sybase锁的类型及作用
(一)共享锁(Share Lock)
共享(S)锁允许并发事务读取(Select)一个资源。资源上存在共享锁时,任何其他事务都不能修改数据。Sybase对读操作加Share锁。如果一个S锁已经加在一个表、数据页、数据行或索引页上,即使加锁的事务是活动的,其他事务仍然可以获得S锁。但在表或数据页或数据行上的所有S锁被释放前,其他事务都不能获得该表或页面或行的exclusive锁。这就意味着多个事务可以同时读表、页面或行,但没有事务能对已经加S锁的表、页面或行中的数据进行修改,而必须等待S锁释放后才能继续。
(二)排他锁(Exclusive Lock)
排他(X)锁是为修改数据而保留的,可以防止并发事务对资源进行访问。排他锁与其他锁不兼容。Sybase对数据更改操作加Exclusive锁。如果一个事务获得了一个Exclusive锁,那么在这个事物结束时释放Exclusive锁之前,其他事务在相应的表或页面或行上不能获得任何类型的锁。其他事务必须等到Exclusive锁释放后才能继续。
(三)更新锁(Update Lock)
一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享锁,然后修改行,此操作要求锁转换为排他锁。如果两个事务获得了资源上的共享模式锁,并试图同时更新数据,则其中一个事务会尝试将锁转换为排他锁。因为一个事务的排他锁与其他事务的共享模式锁不兼容,所以从共享模式到排他锁的转换必须等待一段时间。而此时,第2个事务也试图获取排他锁以进行更新,于是就出现同时持有共享模式锁的两个事务彼此等待对方释放共享模式锁的情形,从而发生死锁。要避免这种潜在的死锁问题,就要使用更新锁.一次只有一个事务可以获得资源的更新锁。如果事务修改资源,则更新锁转换为排他锁,否则转换为共享锁。Sybase在Update,Delete或Fetch的初始阶段加Update锁。如果页面或者行的内容需要修改,只要没有其他Share锁在上面,Update锁立即升级为Exclusive锁。
(四)意向锁(Intent Lock)
意向(I)锁指出在一个表上有页级或行级锁。Sybase对每一个有Share或Exclusive的页或行锁的表加意向锁。所以意向锁可以是Exclusive
锁,也可以是Share锁。设置意向锁可以防止其他后来的事务在有锁住的页的表上获得冲突的表级锁。意向锁持续的时间和事务中页或行锁的时间一样长。
二、melinets死锁的产生与预防
一般来说,如果事务T1封锁了数据R1,T2封锁了R2,然后TI又请求封锁R2,因为R2已经被T2封锁,于是TI等待T2释放R2,上的锁。接着T2又申请封锁R1,因为TI已经封锁了R1,于是T2也只能等待T1释放R1上的锁。这样一来,就出现了T1封锁R1同时等待R2,T2封锁R2同时等待R1。于是T1,T2两个事务永远不能完成,就形成死锁。
例如在应用melinets集中进行数据批量发送过程中,经常容易引起死锁。melinets数据发送过程实际就是将已编目好的书目表数据向流通典藏表更新的过程,批量发送就相当于批量更新。尤其是在同一时间段进行批量发送的用户越多,这时就更容易造成用户之间对表锁和数据页锁的争用,从而加大了死锁的发生。
死锁的频繁发生将极大影响工作的效率,甚至可能对系统造成无法挽回的损失,因此我们应尽量预防发生死锁。防止死锁的发生其实就是破坏死锁的条件。理论上预防死锁有两种方法:
1.一次封锁法。一次封锁法,要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行。显然一次封锁法可以有效的防止死锁的发生,但由于一次将以后要用到的全部数据加锁,扩大的封锁的范围,也就降低了系统的并发度。
2.顺序封锁法。顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。顺序封锁法也可以有效的防止死锁,但它同样存在问题。由于数据库中封锁的对象多,而且事务的封锁请求可以随着事务的执行而动态的决定,因此要维持这样的封锁顺序不但成本很高,而且很难实现。
针对melinets实际应用过程中,我们可以通过合理调度不同任务的执行顺序和时间,避免资源过多占用,这样也可以有效的预防死锁的发生。例如:在进行批量发送时,分批次分时间段进行。同时利用系统过程对数据库相关参数进行合理设置:
(1)带有加锁方式的读提交设置,通过改变释放共享锁的时间点来减少死锁的发生,命令如下:
Sp_config 'read committed with lock',0|1
(2)在考虑扫描一张表之前,需要确定在一张表中能有多少页,采用如下命令在页级结构中加以配置:
Sp_chgattribute
(3)适当设置临时表空间大小,避免临时表空间被占满而发生阻塞。
另外,数据库死锁还与系统综合性能有关。如系统I/O通常是影响系统性能的瓶颈。如果能提高I/O性能,事务执行时间相对缩短,从而锁争用和死锁的可能性就随之减少。
三、死锁的解除
在事务和锁的使用过程中,死锁是一个不可避免的现象。当死锁发生时,我们应尽快查找死锁的原因并解除死锁。通常,当系统检测到死锁发生时,采用的方法是:选择一个处理死锁代价最小的事务,将其撤消,释放此事务持有的所有的锁,使其他事务得以继续下去。当然对撤消的事务所执行的数据修改操作必须加以恢复。针对melinets图书管理系统具体方法如下:
1.死锁发生时用系统进程SP_WHO查看系统进程信息。
SP_WHO返回结果如图(1),结果解释如下:
Fid进程所属的组
Spid 进程号
Status进程状态
Loginname启动进程的用户登录名
Blk阻塞进程的ID
Dbname该进程访问的数据库
Cmd 该进程执行的命令和进程
如果用户进程正被另一用户进程阻塞,则status列显示“lock sleep”,而blk列显示保持该锁或这些锁的进程标识,即被谁锁定。Loginname列显示登录操作员。这样结合相应的操作员信息表,便可知道操作员是谁。
2.用SP_LOCK获得当前状态中锁的分配和使用情况。
通过SP_LOCK命令可以查看系统被锁数据表的情况,包括数据表的ID号。被锁的类型(前缀sh为共享锁,ex为排它锁,后缀blk表明该进程正在阻碍另一个需要请求锁的进程)以及数据库名称dbname。
3.结合二者结果与库表SYSOBJECTS查出被锁定的库表以及锁住别人的操作员。
4.询问相关操作员并撤消相关事务,从而达到解除死锁的目的。
当然,当死锁发生时,重新启动melinets数据库服务器也可以达到解除死锁的目的,但这是个笨方法,因为这样既无法保证数据的完整性,也耗费资源。因此作为数据库DBA而言不到万不得以不应该采用重启的方式。
四、结语
死锁是任何数据库系统都无法避免的一种现象,频繁的死锁将为我们的工作带来很大的不便,甚至是无法估计的损失。因此我们应该尽量减少死锁的发生。而一旦死锁发生时,应该积极采取有效的应对措施来解决死锁。melinets作为一款现代信息集成图书管理系统,在使用过程中或多或少由于SYBASE数据库的特性,或者一些其他的人为因素也可能会发生死锁,但只要我们了解死锁的原理,再根据实际情况,通过合理的调度,就可以尽量减少由于死锁而为我们带来的麻烦。
参考文献:
[1]萨师宣、王珊,数据库系统概论[M].第三版,高等教育出版社,2004.
[2]SYBASE,数据库维护参考手册,Version2.0.
[3]连宇江、唐颖,Melinets图书管理系统的管理和维护点浅谈[J].科技文献信息管理,2005.1.