记一次library cache lock/library cache pin导致的函数编译hang住分析及处理过程

一、前言

业务在进行alter function my_function_name compile的时候,正常来说会非常快(不涉及无法访问的dblink时),但是今天一大早,业务就告诉跟我说,他们有两个函数编译无法通过,现象就是会hang住,需要我进行分析并处理下问题。

二、问题排查

看到这个问题的时候,最初就是认为,数据库肯定是有锁了,导致这个编译过程的语句,执行无法通过。所以,第一时间检查了下数据库中是否存在行锁:

SQL> @block

no rows selected

可以看到,此时并没有排查到数据库有行锁。

此时,我在sqlplus中,手动执行该函数的编译过程:

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

       SID STATISTIC#      VALUE
---------- ---------- ----------
      4994          0          0

SQL>alter function XXX compile;

确实,发现编译过程行住了。

接着,打开另一个会话,查看该会话的运行情况、等待事件及阻塞情况:

SQL> select sid,seq#,event,wait_class from v$session_wait where sid=4994;

                                           WAIT
       SID       SEQ# EVENT                CLASS
---------- ---------- -------------------- ---------------
      4994         41 library cache lock   Concurrency

可以发现,执行函数编译的会话,被sid为368的会话阻塞,而该会话的等待事件则是library cache lock,且wait_class为Concurrency。

那么,在什么情况下,可能会造成library cache lock等待事件,并且会阻塞住会话呢?

从官方文档上可以看到对library cache lock等待事件有如下描述:

This event controls the concurrency between clients of the library cache. It acquires a lock on the object handle so that either:

  • One client can prevent other clients from accessing the same object.

  • The client can maintain a dependency for a long time (for example, so that no other client can change the object).

This lock is also obtained to locate an object in the library cache.

Library cache lock will be obtained on database objects referenced during parsing or compiling of SQL or PL/SQL statements (table, view, procedure, function, package, package body, trigger, index, cluster, synonym). The lock will be released at the end of the parse or compilation.

Cursors (SQL and PL/SQL areas), pipes and any other transient objects do not use this lock.

Library cache lock is not deadlock sensitive and the operation is synchronous.

从上述描述中,可以理解为,library cache lock/pin是当两个客户端同时访问(修改)同一个数据库对象的时候(通常为table, view, procedure, function, package, package body, trigger, index, cluster, synonym等等),率先访问该对象的会话则会在对象上得到一个library lock,从而防止其他的会话访问该对象或者修改、定义该对象。而后一个访问(修改)的会话,则会等待事件’library cache pin’和’library cache lock’。

另外,我们需要知道的一点是:
library cache pin/lock都会有三种模式:

  • NULL
  • SHARE
  • EXCLUSIVE

当读取一个数据库对象的时候,会话会获得一个SHARE模式的lock/pin;
当修改一个数据库对象的时候,会话会获得一个Exclusive模式的lock/pin;

而share的lock/pin则会阻塞exclusive的lock/pin,从而产生library cache pin/lock相关的等待事件。

针对于不同的操作,其获得的锁如下:
所有的DDL操作(包括对过程、函数、视图等编译)都会对访问对象请求exclusive模式的lock和pin;

通过上述的描述,我们其实大概可以看出一些造成编译无法成功的原因了,就是存在其他的会话,在访问这个function对象。
因此,我们可以进一步查看,这个会话是否有其他会话阻塞:
这里可以通过多种方式来查看:
方法一:查看是否有其他会话访问这个函数:

select sid,owner,object,type from v$access where object='&function_name';

但是,该方式查询的速度比较慢,不建议使用;

方法二:通过v$session来查看该会话是否被其他会话阻塞:

SQL> select sid,seq#,blocking_session,wait_class from v$session where sid=4994;

                                           WAIT
       SID       SEQ# BLOCKING_SESSIO EVENT                CLASS
---------- ---------- --------------- -------------------- ---------------
      4994         41             368 library cache lock   Concurrency

方法三:通过脚本

SQL>@we.sql

.....
   library cache lock sqlplus@resdb02 (TNS V  SYS|514|41             4994:16947:50814          A.W..51KS  ALT C.732shxcb178fv:0  F.2.368         515   oracle@[email protected]    -1:0:0:0
.....

此时,我们通过上述方式都可以看到,我们进行alert function XXXX compile的时候,该会话是被SID为368的会话阻塞了。

那么368这个会话到底在做什么呢?是在访问这个函数么?
我们通过v$session也可以找到这个会话的sql_id,并查看该sql_id的sql_text:

select sql_id from v$session where sid=368;
=========================================================
71bwdwbbwsrx5

select sql_text from v$sql where sql_id='71bwdwbbwsrx5';
=========================================================
begin  
-- Call the procedure   
p_test_XXXXk(v_anid =>:v_anid);
end;

可以看到,这个SQL主要是调用了p_test_fn_check_datatrunk过程,通过getddl方式查看该过程的DDL:

SELECT DBMS_METADATA.GET_DDL('PROCEDURE','P_TEST_FN_CHECK_XXXX','CHENGDU') FROM DUAL;
================================================================================================
  CREATE OR REPLACE PROCEDURE "RES_CL"."P_TEST_XXXX" (v_anid in varchar2) as
       v_result varchar2(100);
begin

   v_result := chengdu.XXXXX(v_anid);
end;

此时,就可以看到,该过程使用了这个函数。

三、问题根源

所以,整个链条就很清晰了,即
1)session 368,调用了P_TEST_FN_CHECK_XXXX过程,而P_TEST_FN_CHECK_XXXX过程则会访问XXXX函数;此时,数据库在XXXX函数上获取了share的library cache的lock。
2)当新的session 4994,对函数XXXX进行编译的时候,则会尝试获取EXCLUSIVE的lock。
3)而share的lock会阻塞exclusive的lock。因此,session 4994的compile操作被阻塞住,无法正常执行。而在5分钟后,则会产生ORA-4021的错误,超时中断。

四、问题解决

异常链条清晰后,问题处理就变的非常简单了,我查询了368这个会话的状态,发现它还是active的,并告知用户。在用户同意的情况下,将该会话kill掉。重新尝试编译,则发现编译正常完成。

五、总结

通过本次异常处理,可以对library cache pin和library cache lock的等待事件的原理进行深入的了解,知道了在什么时候library cache会获取什么样的lock或者pin,帮助我们分析处理问题。

猜你喜欢

转载自blog.csdn.net/wx370092877/article/details/109596329