判断对象是否被锁以及被锁的真正原因 侵立删

在Oracle中不管我们执行插入,更新,删除等操作时候,必然都会锁住相关的数据,这种锁是属于正常的锁。只有当我们操作的数据所需要的锁被其他会话持有的时候,这时候才会产生真正意义上锁争用的问题,也就是只有多用户(多会话)的情况下才可能发生锁争用的问题。

开发,测试和运维应该都碰到过表锁住的情况,针对表锁住的处理方式,相信大家都会按照互联网的攻略直接找到锁住的对象,然后直接杀死被锁的对象的会话。这种情况在开发测试过程中问题不大,但是如果已经上了生产而且一个对象可能存在多个会话锁住的情况就会很糟糕了。以简单例子来说明

首先打开一个窗口查询SID 得到SID为137

select userenv('sid') from dual;

                                               blob.png

在该会话更新SQL但是不提交结果

update/*+ sql_a */  pbb_test set content = content||'' where id = 2000;

打开另一个窗口查询SID 得到SID为131

select userenv('sid') from dual;

blob.png

在该会话执行SQL,这时你会发现该会话一直在执行中

update /*+ sql_b */ pbb_test set content = content||'' where id = 2000;

blob.png

通过以下SQL查询,可以发现

select t.SID,t.EVENT,t.SQL_ID  from v$session t where t.SID in (131,137)

131被锁住了,而137现在属于空闲状态

blob.png

实际上137虽然已经更新完毕,但是它没有提交,进而导致131的更新被阻塞了,所以我们现在要做的是找到的是137被什么阻塞了。如果我们按照网上的攻略来做?会出现什么情况呢?

通过以下语句我们可以发现被锁的对象

select b.owner, b.object_name, a.session_id, a.locked_mode

  from v$locked_object a, dba_objects b

 where b.object_id = a.object_id;

blob.png

有两个会话,分别是137和131锁住了PBB_TEST

然后通过以下SQL查询找到SID和SERIAL#

select b.username,b.sid,b.serial#,logon_time

 from v$locked_object a,v$session b where a.session_id = b.sid orderby b.logon_time;

blob.png

这时候大部分开发可能会通过以下命令来杀死会话

alter system kill session'SID,SERIAL#';

那么问题来了,这时候到底是杀死137还是131呢?如杀死131,那么再次执行还会发生锁等待,如果杀死137则问题解决。如果两个都杀问题一样会解决。但是如果不止两个会话怎么办?就像这样

blob.png

怎么办?全杀了?这可是生产啊。网上的攻略只到这里了。大部分运维心里开始发慌了。根据以往运维的求助经验来看这时候大部分的运维是怎么干的呢?

首先打开会话,然后杀死所有活动会话,

blob.png

好了。问题终于解决了。然后一跑结果又锁住了。为什么?因为137已经不活动了啊。没杀掉。

其实处理这个问题真的非常简单。只要直接看会话就可以了。根本没有必要像上面那么复杂的去求证。还是以131和137加几个其他会话

selectt.SID,t.EVENT,t.SQL_ID,t.PREV_SQL_ID,t.BLOCKING_SESSION,t.WAIT_CLASS  from v$session t where t.SID in (131,137,138,139,140)

blob.png

通过上面语句查询出结果然后查看131会话,可以看到目前的等待事件是 enq:TX – row lock contention这个事件明显的表示现在存在锁争用,因为什么争用呢?可以看到WAIT_CLASS是Application说明在应用层面导致了争用,然后再看到底是哪个会话导致了131的锁呢?这时候要看BLOCKING_SESSION可以发现是被137阻塞了。其他会话也是一样被137阻塞了,然后看137会话,可以看到不管是事件还是等待Class都已经是空闲状态了。(你也可以用PREV_SQL_ID去查询上一次执行的SQL到底是什么,当然大部分情况,可能没啥用)所以这时候你应该结束137。如果不是明显的应用程序设计错误,结束掉137之后后续的会话应该都可以正常运行了,如果137在执行SQL即SQL_ID有值,并且没有被其他会话阻塞则不要结束会话,因为他只是执行比较慢。你应该在事后优化SQL。

blob.png

当然在本例子中是使用更新不提交的方式,所以137结束之后其他会话又被131阻塞了。如果生产上也有类似情况你就需要根据BLOCKING_SESSION一步步的去解锁相关的会话。

猜你喜欢

转载自blog.csdn.net/qq_22167989/article/details/82191434
今日推荐