Oracle中的死锁Dead Lock(一)

几天前和一位做应用的同事讨论,聊到了死锁。笔者觉得很有意思,就把讨论中的一些想法和知识拿出来和大家分享。

 

死锁Dead Lock

 

我们大家最早接触死锁这个概念可能是在操作系统课程中,说多个进程(线程)对一个可共享的资源进行请求的时候,可能出现死锁。死锁问题分为死锁检测、处理等多个子问题可以进行讨论。

 

其实,死锁问题绝不仅仅限制在操作系统乃至计算机科学领域。死锁存在的两个必要条件,一个是多任务工作的并发,另一个是共享资源的独占性需求。只要一个系统(广义系统)中存在这两个前提,我们就认为可能出现死锁的情况。

 

死锁描述的是一种状态。当两个或两个以上的任务单元在执行过程中,因为请求资源出现等待,因资源永远不能获得而相互等待的状态。如果没有外力的作用,死锁状态是会一直持续下去。死锁是伴随着多任务、并行操作产生的,在单任务情况下,一个任务单元可以使用并且独占所有资源,不存在资源等待的情况,所以也没有死锁的情况。在进入多任务系统环境下,多个任务之间存在资源共享和独占的需求,才可能出现死锁。

 

死锁最简单的例子:任务AB,资源12。任务A独占了资源1,任务B独占了资源2。此时,任务A要资源2,向任务B提出请求并等待。任务B要求资源1,并且也等待。AB两者均不释放所占有的资源,就造成了死锁。

 

 

Oracle中的死锁

 

Oracle是目前商业数据库市场上表现最优秀的并发数据库系统,同样存在死锁的威胁。存在并发、存在资源独占,就有锁lock或者类似锁概念的机制。Oracle提供了多种类型锁和多种锁的机制,包括共享锁和独占锁。并且,在进行各类型操作的时候,自动的对对象进行加锁解锁,以及锁升级操作,最大可能的保证数据完整性。

 

那么,如果出现死锁,Oracle会如何处理呢?

 

Oracle的锁机制是建立在行锁一级,在插入、更新行一级信息的时候,会加入独占锁内容。那么,我们尝试模拟下出现死锁的状态。两个session分别更新两条记录,在一个事务里再尝试更新对方记录,那么可以引发死锁。

 

实验环境准备:

 

SQL> desc t;

Name Type         Nullable Default Comments

---- ------------ -------- ------- --------

ID   NUMBER                                 

COMM VARCHAR2(10) Y                        

 

SQL> select * from t where rownum<3;

 

        ID COMM

---------- ----------

         1 Tst1

         2 Tst2

 

两个session,分别针对id=1,2两条记录做文章。

 

//Session1

SQL> select sid from v$mystat where rownum<2;

 

       SID

----------

       152

 

SQL> update t set comm='Tst1' where id=1;

 

1 row updated

 

//Session2

SQL> select sid from v$mystat where rownum<2;

 

       SID

----------

       150

 

SQL> update t set comm='Tst2' where id=2;

 

1 row updated

 

此时,锁的状态为:

 

SQL> select * from v$lock where sid in (150,152);

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST

-------- -------- ---------- ---- ---------- ---------- ---------- ----------

333415A4 333415BC        152 TM        54599          0          3          0

33341668 33341680        150 TM        54599          0          3          0 

3338A42C 3338A548        150 TX       393251        795          6          0 

333B8954 333B8A70        152 TX       327686        779          6          0

 

此时,我们看到了在两个session中,分别使用了行锁,独占(LMODE=6)锁住了两行。下面继续相互请求。

 

//session1中,尝试更新id=2的记录

SQL> update t set comm='Tst2' where id=2;

 

Session1SID=152)被hange住。

 

//锁状态

SQL> select * from v$lock where sid in (150,152);

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST

-------- -------- ---------- ---- ---------- ---------- ---------- ----------

33834398 338343AC        152 TX       393251        795          0          6 

333415A4 333415BC        152 TM        54599          0          3          0 

33341668 33341680        150 TM        54599          0          3          0 

3338A42C 3338A548        150 TX       393251        795          6          0

333B8954 333B8A70        152 TX       327686        779          6          0 

 

说明:在Oracle中,hange表示session正在等待资源被释放,表现就是停住操作,不断的轮询资源。

 

Session2请求更新:

 

//session2SID=150

SQL> update t set comm='Tst1' where id=1;

 

注意:此时,应当出现死锁状态,而在系统中,也出现了一瞬间的两个session互相hange的状态;

 

此时,session2继续被hange住,原先的session1退出,如下状态:

 

//session1SID=152

SQL> update t set comm='Tst2' where id=2;

 

update t set comm='Tst2' where id=2

 

ORA-00060: 等待资源时检测到死锁

 

结果是:Oracle在两个出现死锁的session中,随机寻找了一个session,驳回了其被hange住的请求,维持另一方sessionhange状态。

 

注意:Oracle此处的处理:只是驳回了一方的请求,并没有回滚该请求,也没有将另一方的hange状态解除。

 

思考:显然,这个过程中是Oracle内部的防护机制起了作用,防止了系统中死锁的发生。在Oracle中,存在某种轮询的机制,随时检查系统中出现的多会话被hange住的情况,一旦发生,就将一个sessionhange住的请求退回,抛出00060错误。

 

在两个session互锁的情况下,Oracle死锁检测程序可以起作用。那么,如果死锁结构复杂的时候,是不是Oracle的检测机制会失效。笔者使用4-5session进行检查,虽然检测起效的时间有长有短,但最后都是将死锁的状态加以解除。

 

有一点需要注意,Oracle解决死锁的方式只是将请求拒绝,并不是将事务回滚。所以,在解决死锁之后,其他被hange住的session依然还是被hange住。所以,从应用程序和PL/SQL的角度,如果接受到了60错误,应该做的工作就是回滚当前事务,解决整体的资源争用现象。

 

 

Oracle死锁发生

 

那么,Oracle中死锁发生的几率是不是那么高呢?答案是否定的。这个是由Oracle锁的特性所决定的。

 

1、  Oracle对查询不加锁。Oracle本身支持多版本一致读,如果当前的数据块正在被修改(独占)而并未提交,Oracle会根据SCN查找日志和Undo空间,找到合适SCN的版本返回结果。所以,在查询的时候,是不需要加锁的。

2、  Oracle数据操作使用行级锁(本质上是事务锁),实现最小粒度的独占范围。OracleDML的时候,只会对操作的行进行独占锁定,而不是过大的数据单元(如页page)。这样,就保证了不会引起过多的独占资源。

 

同时,Oracle本身也提供了死锁监视程序功能,能及时发现死锁状态,并自动的进行解锁。在这些机制下,Oracle认为死锁发生的概率很低(起码自身不会引起死锁)。只有一种情况会引发死锁,就是开发人员手工提高加锁的级别。

 

Oracle对于SQL的拓展中,有一部分是显示对象加锁。从lock table XXXselect XXX for update。这些操作都会引起对对象锁级别的提升,这些都可能引发死锁的发生。

 

 

那么,如果出现死锁的情况,我们如何处理?在设计应用的过程中,我们如何避免死锁现象? 留待下次继续讨论。


http://blog.itpub.net/17203031/viewspace-682115/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值