编译存储过程卡死主因是ddl锁冲突,而非dml锁;应查dba_ddl_locks和gv$session定位持有者,用alter system kill session ... immediate清理,并验证锁是否释放。

查 v$locked_object 只能看到 DML 锁,但编译卡住不一定是它
存储过程编译卡住,第一反应查 v$locked_object 很常见,但这个视图只记录 DML 类锁(比如 UPDATE/DELETE 持有的行锁),而编译动作触发的是 DDL 锁。如果查不到记录,不代表没锁——大概率是锁在 dba_ddl_locks 里。
真正该跑的语句是:
SELECT session_id, owner, name, type, mode_held FROM dba_ddl_locks WHERE name = 'YOUR_PROCEDURE_NAME';
注意替换 YOUR_PROCEDURE_NAME 为实际过程名,大小写敏感;若不确定具体名,可先用 SELECT * FROM dba_ddl_locks WHERE mode_held != 'Null' 看活跃 DDL 锁。
确认阻塞源头必须看 gv$session(尤其 RAC 环境)
RAC 下只查 v$session 极易漏掉其他节点上的持有者。正确做法是用 gv$session 关联 dba_ddl_locks 找出完整链路:
SELECT s.sid, s.serial#, s.username, s.program, s.event, s.sql_id FROM gv$session s, dba_ddl_locks d WHERE s.sid = d.session_id AND d.name = 'YOUR_PROCEDURE_NAME';
重点看 event 字段:如果是 enq: DD - contention 或 library cache lock,基本锁定是 DDL 锁冲突;sql_id 能帮你反查原始语句(用 v$sql),确认是不是未提交的 ALTER/CREATE 操作卡在半途。
常见陷阱:
-
blocking_session为空,但会话状态是ACTIVE且event是 TX 类等待 → 它自己持锁没提交,不是被别人堵,而是它堵了别人 - 查到的会话
status是KILLED,但 SPID 还活着 → 必须查v$process拿到 OS 层进程号,再用kill -9彻底清理
kill session 要带 immediate,且 RAC 必须指定实例号
执行 alter system kill session 不加参数,只是标记终止,客户端可能继续 hang 住几秒甚至几分钟。生产环境务必加 immediate:
ALTER SYSTEM KILL SESSION '123,456' IMMEDIATE;
RAC 环境下,漏掉实例号会导致命令发错节点,锁还在原地:
ALTER SYSTEM KILL SESSION '123,456,@2' IMMEDIATE;
其中 @2 表示实例 2。实例号可通过 gv$session.inst_id 查得。别信 program 字段就杀——同一个应用可能有多个合法长事务,先用 v$sql.sql_text 确认是不是 ALTER PROCEDURE ... COMPILE 卡住,再动手。
DDL 锁残留比 DML 锁更难清理,别跳过验证步骤
DML 锁通常随事务结束自动释放;DDL 锁(尤其是排他锁)可能因客户端异常断开、PL/SQL Developer 强制退出、网络中断等导致锁残留,且不会自动清理。所以每次 kill 后,必须重新查:
-
SELECT * FROM dba_ddl_locks WHERE name = 'YOUR_PROCEDURE_NAME'→ 应返回空 -
SELECT * FROM v$db_object_cache WHERE name = 'YOUR_PROCEDURE_NAME' AND locks != '0'→ LOCKS 值应为 0 - 再试编译,仍卡?说明还有隐式依赖对象被锁(比如过程里引用的表正被 ALTER)→ 需扩大范围查
dba_ddl_locks的owner和type
最麻烦的情况是:锁源头会话已消失,但内核态锁结构没回收。这时只能重启数据库实例,或联系 DBA 用 oradebug 强制清除 —— 这类操作不可逆,务必留痕并评估业务影响。











