介绍
今天我们来介绍下ORA-00060: deadlock detected while waiting for resource这个死锁异常。
我们在开发的时候对数据库资源如果不注意会造成资源互相竞争的情况,例如两个事务分别竞争对方已持有的资源,这个时候Oracle是会自动检测并报ORA-00060: deadlock detected while waiting for resource然后回滚该事务,之后另外一个事务则会继续执行,从而避免继续互相等待。下面我们来实际模拟一下这种情况。
使用for update模拟ORA-00060报错
首先我们打开两个oracle窗口(相当于两个oracle连接)。我们需要将窗口事务提交设置改为手动(Manual)。执行步骤如下:
1.窗口A:
select AGT_MERC_ID from URMTAGTLVE where AGT_MERC_ID = 'S43100112055050' for update ;
2.窗口B:
select AGT_MERC_ID from URMTAGTLVE where AGT_MERC_ID = 'S43100112072935' for update;
3.窗口A:
select AGT_MERC_ID from URMTAGTLVE where AGT_MERC_ID = 'S43100112072935' for update ;
4.窗口B:
select * from URMTAGTLVE where AGT_MERC_ID = 'S43100112055050' for update;
当执行第3步窗口A的时候你会发现该for update 锁定查询会一直等待,这是因为窗口B中事务已经锁定了AGT_MERC_ID为S43100112072935的记录,则窗口A事务需要等待该资源。
当执行第4步窗口B的时候由于该资源已经被窗口A中事务锁定所以会处于一直等待状态,这时窗口A你会发现报如下异常:
出现以上异常的时候窗口B中的执行(也就是步骤4)依然在等待资源。因为窗口A会话中的ORA-00060错误不会提交或回滚,它会保留以前的锁,包括死锁场景中涉及的锁。这个时候就要客户端干预了,来提交或者回滚事务。
如果在项目中异常后那就需要回滚了, 我们在窗口A中执行rollback手动回滚下: 窗口A:
rollback ;
回滚后我们会发现窗口B的事务不会再等待,而是会继续执行完成并提交事务。可见oracle在这种情况就是通过检测ORA-00060异常来处理死锁的,但是只是报错,需要客户端负责提交或回滚。
总结
死锁都是由多个"会话"争用相互锁定的资源导致的。本文简单模拟了由于死锁造成ORA-00060异常的某种情况,我们应该尽量避免死锁而不是补救,必须确保会话以相同的"顺序"执行其操作,如事务中先更新表T1再更新表T2,那么任何其它进程都以相同的顺序执行此操作。