DDL锁定具有以下三种类型:
1、 排他的DDL锁定(Exclusive DDL Lock)
大部分的DDL操作都会在被操作的对象上添加排他的DDL锁定,从而防止在DDL命令执行期间,对象被其他用户所修改。当对象上添加了排他的DDL锁定以后,该对象上不能再添加任何其他的DDL锁定。如果是对表进行DDL命令,则其他进程也不能修改表里的数据。
2、共享的DDL锁定(Shared DDL Lock)
用来保护被DDL的对象不被其他用户进程所更新,但是允许其他进程在对象上添加共享的DDL锁定。如果是对表进行DDL命令,则其他进程可以同时修改表里的数据。比如我们发出create view命令创建视图时,在视图的所引用的表(这种表也叫基表)上添加的就是共享的DDL命令。也就是说,在创建视图时,其他用户不能修改基表的结构,但是可以更新基表里的数据。
3、可打破的解析锁定(Breakable Parsed Lock)
在shared pool里缓存的SQL游标或者PL/SQL程序代码都会获得引用对象上的解析锁定。如果我们发出DDL命令修改了某个对象的结构时,该对象相关的、位于shared pool里的解析锁定就被打破,从而导致引用了该对象的SQL游标或者PL/SQL程序代码全都失效。下次再次执行相同的SQL语句时,需要重新解析,这也就是所谓的SQL语句的reload了。可打破的解析锁定不会阻止其他的DDL锁定,如果发生与解析锁定相冲突的DDL锁定,则解析锁定也会被打破。
我们主要通过dba_ddl_locks视图来监控DDL锁定,没有与DDL锁定相关的V$视图。如果没有发现dba_ddl_locks视图,则执行脚本$ORACLE_HOME/rdbms/admin/catblock.sql来创建该视图,执行脚本时应该以用户sys的身份登录数据库。
上文中介绍到的Oracle中的DDL锁使Oracle数据库中的数据安全得到了保障,避免数据库中数据的丢失或者被人乱改。
在数据库的开发过程中,经常碰到包、存储过程、函数无法编译或编译时会导致PL/SQL 无法响应的问题。碰到这种问题,基本上都要重启数据库解决,严重浪费开发时间。本文将就产生这种现象的原因和解决方案做基本的介绍。
- 问题分析
从事数据库开发的都知道锁的概念,如:执行 Update Table xxx Where xxx 的时候就会产生锁。这种常见的锁在Oracle里面被称为DML锁。在Oracle中还有一种DDL锁,主要用来保证存储过程、表结构、视图、包等数据库对象的完整性,这种锁的信息可以在DBA_DDL_LOCKS中查到。注意:V$LOCKED_OBJECT记录的是DML锁信息,DDL锁的信息不在里面。
对应DDL锁的是DDL语句,DDL语句全称数据定义语句(Data Define Language)。用于定义数据的结构或Schema,如:CREATE、ALTER、DROP、TRUNCATE、COMMENT、RENAME。当我们在执行某个存储过程、或者编译它的时候Oracle会自动给这个对象加上DDL锁,同时也会对这个存储过程所引用的对象加锁。
了解了以上知识以后,我们可以得出结论:编译包长时间无响应说明产生了死锁。我们可以轻易的让这种死锁发生,举例:
1、 打开一个PL/SQL,开始调试某个函数(假设为:FUN_CORE_SERVICECALL),并保持在调试状态
2、 打开一个SQL Window,输入Select * From dba_ddl_locks a Where a.name = 'FUN_CORE_SERVICECALL'会发现一行记录:
3、 打开一个新的PL/SQL,重新编译这个函数。我们会发现此时已经无法响应了
4、 回到第一个PL/SQL ,重新执行Select * From dba_ddl_locks a Where a.name = 'FUN_CORE_SERVICECALL'我们将会看到如下记录:
5、 上述的情况表明发生了锁等待的情况。
在Oracle中DDL锁分为:Exclusive DDL Locks(排他的DDL)、Share DDL Locks(共享DDL锁)、Breakable Parse Locks(可被打破的解析锁)几类。篇幅所限,这里就不再详细介绍了。根据这个例子推理一下,当我们试图编译、修改存储过程、函数、包等对数据对象的时候,如果别人也正在编译或修改他们时就会产生锁等待;或者我们在编译某个存储过程的时候,如果它所引用的数据库对象正在被修改应该也会产生锁等待。这种假设有兴趣的兄弟可以测试下,不过比较困难。
- 解决方案
碰到这种问题,如果知道是被谁锁定了(可以查出来的),可以让对方尽快把锁释放掉;实在查不出来只能手工将这个锁杀掉了。步骤如下:
1、 首先查出哪些进程锁住了这个对象,语句如下:
Select b.SID,b.SERIAL#
Fromdba_ddl_locks a, v$session b
Where a.session_id= b.SID
And a.name ='FUN_CORE_SERVICECALL';
2、 执行如下语句杀进程:alter system kill session 'sid,serial#'
3、 执行了以上的语句后,有的时候不一定能够将进程杀掉。这个时候就需要连到数据库服务器上杀掉服务器端的进程了,查询语句:
Select spid, osuser, s.program
Fromv$session s, v$process p
Where s.paddr= p.addr
And s.sid =(上面查出来的SID)
4、 在服务器上执行如下语句:
#kill -9 spid(UNIX平台)
orakill sid thread(Windows平台SID是Oracle的实例名,thread是上面查出来的SID)
5、 执行完4步以后基本上就可以杀掉这些锁死的进程了,不放心的话可以再执行第一步确认下。
本文只能是说对这个问题做了初步分析,产生此问题的原因及解决方案涉及到很多Oracle的基本知识。如:DML语句,DDL语句;Oracle锁的机制;v$session 与 v$process之间关系等。
更新包或其他对象时,如果包或其他对象被引用,则Oracle会产生一个DDL锁,进程则处于等待状态,利用以下SQL可以查看数据库当前的存在的DDL锁,可以根据CMD字段中的命令,将引起锁的进程结束。
- CREATE OR REPLACE VIEW XT_V_LOCKSOBJECT AS
- select o.owner,o.object_name,s.sid,s.serial#,s.USERNAME,s.STATUS,s.machine,s.PROGRAM,'alter system kill session ' || chr(39) || to_char(s.SID) || ',' || to_char( s.SERIAL# ) || chr(39) || ';' as cmd
- from v$locked_object l,dba_objects o ,v$session s
- where l.object_id = o.object_id
- and l.session_id=s.sid --and s.STATUS = 'ACTIVE'
- union
- SELECT DISTINCT "SYS"."DBA_DDL_LOCKS"."OWNER",
- "SYS"."DBA_DDL_LOCKS"."NAME",
- "SYS"."V_$SESSION"."SID",
- "SYS"."V_$SESSION"."SERIAL#",
- "SYS"."V_$SESSION"."USERNAME",
- "SYS"."V_$SESSION"."STATUS",
- "SYS"."V_$SESSION"."MACHINE",
- "SYS"."V_$SESSION"."PROGRAM",'alter system kill session '|| chr(39) || to_char("SYS"."V_$SESSION"."SID") || ',' || to_char( "SYS"."V_$SESSION"."SERIAL#" ) || chr(39) || ';' as cmd
- FROM "SYS"."DBA_DDL_LOCKS","SYS"."V_$SESSION"
- WHERE ( "SYS"."DBA_DDL_LOCKS"."SESSION_ID" = "SYS"."V_$SESSION"."SID" )
- AND ( SYS."V_$SESSION"."STATUS" = 'ACTIVE' )