在数据库实现中,通过锁定机制控制数据库的并发访问,保证数据库访问的正确性。根据定义:
锁定是一种并发控制机制:它可以确保数据在同一事务中和不同事务之间保持一致。在多用户环境中,由于几个用户可能会在同一时间使用同一数据,因此需要锁定功能。
sybase锁分类
按照锁性质可以分为共享锁,排他锁。当在数据库事务中,读取信息时,会对数据库添加共享锁。当修改信息时,会添加排他锁。
按照锁的粒度,可以分为行锁,页锁,表锁等。
sybase隔离级别
sybase分为0,1,2,3四个隔离级别。
0 读取未提交的,允许事务读取未提交的数据更改,排他锁在对数据库进行写操作后立即释放,不会持有到事务提交或回滚。
1 读取已提交的,仅允许事务读取已提交的数据更改,排他锁持有到事务提交或回滚,但共享锁在加载数据到内存后立即释放。
2 可重复读取事务可重复同一查询,事务读取过的任何行都不会被更新或删除,排它锁和共享锁都会持有到事务结束,查询结果集不可以删除和修改,但是可以插入。
3 可串行化读取事务可重复同一查询,且得到完全相同的结果。不能插入任何将出现在结果集中的行,排它锁和共享锁都会持有到事务结束,查询结果集不可以增删改。
可以使用select @@isolation语句查看数据库的隔离级别。
sybase数据库通常隔离界别设置为1,值得注意的是使用WAS通过jdbc连接数据库上,经常会将隔离级别提升为2。在使用E-SQL编译时,通常将隔离级别提升为3。
sybase死锁
sybase数据库出现死锁,即处于死锁中的各事务,都持有锁,但又都在等待其他锁,从而组成一个环,造成死锁。
最简单的死锁情况,事务T1,T2,执行顺序相反,会造成死锁,情形如下:
执行顺序 | T1 | T2 |
1 | 排他锁A | |
2 | 排他锁B | |
3 | 排他锁B | |
4 | 排他锁A |
这时候,会出现事务T1持有排他锁A,同时等待排他锁B,事务T2持有排他锁B,等待排他锁A。这是就造成了T1等待T2释放排他锁B,T2等待T1释放排他锁A,形成一个死锁环。
多个事务出现死锁时,情形与两个事务死锁相比,只是环更大了一些,环上的节点多了一些。其本质仍然是形成一个等待环。
隔离级别对死锁的影响
隔离级别同样会对锁定有很大的影响,例如,
情形一、
执行顺序 | T1 | T2 |
1 | 排他锁A | |
2 | 排他锁B | |
3 | 共享锁B | |
4 | 共享锁A |
当隔离级别为0时,不会出现死锁。当隔离界别为1,2,3时,则会发生死锁。
情形二、
执行顺序 | T1 | T2 |
1 | 共享锁A | |
2 | 共享锁B | |
3 | 排他锁B | |
4 | 排他锁A |
当隔离级别为0,1时,不会出现死锁。当隔离界别为2,3时,会发生死锁。
情形三、
该情况是最近在系统中发现的一个死锁问题。程序从文件导入数据到数据库中,每次导入一条记录时,首先尝试以update的方式导入一条记录,当找到记录为空时,则将该条记录更改为以insert的方式导入到数据库中。
同时,导入过程是由多个进程共同完成的,每个进程导入一个文件,多个进程同时工作,然而当程序运行时,多个进程同时导入出现死锁。
通过监控sybase日志,发现死锁都是发生在insert时,出现next-key lock。sybase日志保存在安装目录下,例如安装目录为/sybase/ASE-12_5,日志文件为/sybase/ASE-12_5/install/db_name.log。
通过检查数据库的隔离级别,为1,没有发现异常,百思不得其解。
后在程序中添加查询数据库隔离级别语句,以检查在程序运行中到底隔离级别是多少?
经检查,隔离级别为3,也就是说在事务中,不能插入任何将出现在结果集中的行,下面分析一下出现死锁的原因。
当两个进程同时插入记录到同一个间隙中时,每个事务可能由两个操作组成
1.update
2.insert,当update结果集为空时,则转为insert。
其执行过程中,两个进程可能出现以下运行情况
执行顺序 | T1 | T2 |
1 | 共享锁A | |
2 | 共享锁B | |
3 | 排他锁B | |
4 | 排他锁A |
例如,目前数据库只有一条记录,主键为5,此时T1,T2分别插入主键为3,4的数据,由于两个事务都在运行之中,因此T1,T2都会尝试在5之前插入数据,首先其在update时,会产生共享锁,由于隔离级别为3,此时两个事务尝试插入时都会失败,要解决这种死锁,可以在程序中显式设置隔离级别为1。
sybase锁升级
sybase同时提供锁升级的功能,例如将行锁升级为页锁,将页锁升级为行锁。具体参数可以进行设置。
例如当某一页中90%的行都被锁定,那么此时sybase可能将这些多个行锁升级为一个页锁,锁定整个页。这也是造成死锁一个重要的原因。
有时,根据判断,不会产生死锁。
执行顺序 | T1 | T2 |
1 | 行级排他锁A | |
2 | 行级排他锁B | |
3 | 行级排他锁C | |
4 | 行级排他锁D |
在上述情况中,如果没有锁升级机制,是无论如何也不会产生死锁的。但是当有了锁升级机制之后,可能T1在将行级锁A升级为页锁Pa,T2将行锁B升级为页锁Pb,而T1需要访问的行C在页Pb中,T2需要访问的D在也Pa中,这时就会构成一个锁定环,构成死锁。
总结
在软件系统实现中,经常会采用数据库。使用数据库时死锁问题是大家通常都会遇到的,遇到死锁首先需要分析原因,定位问题,这也是最关键的一步。
出现死锁的原因很多,但本质一定是多个进程出现了相互等待,而每个进程又都持有锁,从而形成了一个依赖关系环,因此问题最关键的就是找到这个依赖关系,以及是哪几个事务,哪几个锁导致了死锁,只要确定了这几点,死锁问题将迎刃而解。