包编译卡住的终极解决办法

来源:互联网 发布:一键截图软件 编辑:程序博客网 时间:2024/05/16 11:18

在数据库开发过程中,经常遇到一个很烦躁的现象:刚修改好的包一编译就卡死了,PL/SQL变成一片空白,又不忍心关闭,这可是耗死多少脑细胞才写出来的!!!只好去求DBA帮忙解决,可是人家也不是每次都会耐心帮你解决,结果经常碰一鼻子灰,所以了,求人不如求己,下面就让我们自己学会去解决这个烦人的问题。。

首先我们先了解下与这种卡死现象相关的几个视图:

1)v$access

V$ACCESS displays information about locks that are currently imposed on library cache objects. The locks are imposed to ensure that they are not aged out of the library cache while they are required for SQL execution.

v$access里存放的是数据字典缓存中的对象,包括表,包,游标等(库缓存缓存的是最近执行的代码),所以你当前编译的包是可以查到的,执行完毕后一般就会v$access中消失。不过有种情况要注意下,如果一个session执行了包A后就停下来了(session对应的状态为INACTIVE),执行完毕后,session与包A可能还是存在于v$access中,直到这个session再次执行另外的sql,这种情况是不会影响你包的编译。所以当你看到v$access中有你要编译的包,不要惊慌,它不一定是凶手,去v$session中确认下状态再做判断。

2)DBA_DDL_LOCKS

DBA_DDL_LOCKS lists all DDL locks held in the database and all outstanding requests for a DDL lock.

当你编译包时,oracle就会对这个包加个DDL锁,同时也会这个包所引用的对象加DDL锁(与DDL锁对应的是DML锁,记录在V$locked_object里,sql语句执行完毕后,DML锁不会消失,必须commit或rollback)


下面我们做几个实验来验证上面的理论(其实上面也是我从别处看到的,对不对还得自己去验证)

1. 用PL/SQL随便调试个sp,让它停在调试状态;


2. 看看在两个视图中的情况:

--注意这里用了/*+rule*/可以解决查询 v$access慢的情况

SQL> SELECT/*+rule*/ * FROM v$access t WHERE t.OWNER='ORACLE' AND OBJECT='TEST_FOR_DDL';

151       ORACLE       TEST_FOR_DDL        PROCEDURE


SQL> SELECT * FROM dba_ddl_locks t WHERE t.owner='ORACLE' AND NAME='TEST_FOR_DDL';

151        ORACLE        TEST_FOR_DDL            Table/Procedure/Type                      Null      None


3.再开一个窗口,编译TEST_FOR_DDL,开始卡死;


4.再查两个视图中的情况:发现v$access中显示的认识持有DDL锁的session,而dba_ddl_locks显示了持有锁和请求锁的session

SQL> SELECT/*+rule*/ * FROM v$access t WHERE t.OWNER='ORACLE' AND OBJECT='TEST_FOR_DDL';

151       ORACLE       TEST_FOR_DDL        PROCEDURE


SQL> SELECT * FROM dba_ddl_locks t WHERE t.owner='ORACLE' AND NAME='TEST_FOR_DDL';

151       ORACLE       TEST_FOR_DDL            Table/Procedure/Type                      Null                None
158       ORACLE       TEST_FOR_DDL            Table/Procedure/Type                      Exclusive       None    --排他锁


5.让调试的session跑完,再来看。编译卡死的session死而复生

--v$access记录没了

SQL> SELECT/*+rule*/ * FROM v$access t WHERE t.OWNER='ORACLE' AND OBJECT='TEST_FOR_DDL';

no rows selected

--只剩调试的session,此时仍可以正常编译sp

SQL> SELECT * FROM dba_ddl_locks t WHEREt.owner='ORACLE' AND NAME='TEST_FOR_DDL';


151               ORACLE                    TEST_FOR_DDL                 Table/Procedure/Type                    Null      None


通过上面的实验,我们可以得出这样的结论:v$access,dba_ddl_locks 中有你要编译的对象,都不能说明会影响你的编译。对于v$access,你要确认其对应的session是否ACTIVE,对于dba_ddl_locks,你要确认其对应的锁类型是否为Exclusive。这样就可以准确找到卡死你编译的真凶。。


找到卡死你真凶的sid后,下面就开始准备干掉它:

1. 找出其对应的serial#:

SQL> SELECT SID,SERIAL# FROM v$session  WHERE SID IN (150);

       150         20


2. 杀掉:

SQL> alter system kill session'150,20';

System altered.


3. 杀掉后一切恢复正常,编译卡住的sp也编译过去了

SQL> SELECT SID,SERIAL#,status FROM v$session  WHERE SID IN (150);

no rows selected


不过我这是很幸运的情况,很多时候你kill会话后,PMON并不能及时清除该会话的资源,在V$session中的状态是KILLED,还是会卡住你要编译的包。要是你是开发人员的话,只能求助DBA帮你把session对应的操作系统线程杀掉(因为开发人员通常是没有对操作系统进行操作的权限),不过我们还是应该了解DBA是怎么操作的,其实很简单的:

1. 找出进程地址

SELECT PADDR FROM v$session  WHERE SID IN (147);

2. 找操作系统对应的SPID

SELECT spid FROM V$process WHERE addr='30E1C924'

3.在操作系统杀

linux:          kill pid

windows:    orakill sid spid


这下就肯定没人阻止你编译了,不过小心要是你把别人正在用的session杀掉了,小心挨揍大笑