从ash揪出持锁ddl语句:查gv$active_session_history中event='library cache lock'且sql_opname in ('alter','create','drop')的记录,重点关注blocking_session非空、p1text='handle address'的行;若sql_id为空,则结合session_id关联v$session查sql_text,确认是否为create or replace package等长耗时ddl。
怎么从ash里揪出正在抢锁的ddl语句
ash(active session history)是定位 library cache lock 持锁源头最直接的入口,尤其当等待已持续、阻塞链形成时。关键不是查“谁在等”,而是查“谁在持锁且没释放”——ddl 语句最容易卡在这里。
执行以下查询,聚焦在未提交或编译耗时长的 DDL:
SELECT sql_id, sql_opname, event, p1text, p1, p2text, p2, blocking_session, session_state
FROM v$active_session_history
WHERE event = 'library cache lock'
AND sample_time > SYSDATE - 1/24
AND sql_opname IN ('ALTER', 'CREATE', 'DROP', 'GRANT', 'REVOKE')
ORDER BY sample_time DESC;
重点看 p1text 是 handle address、p2text 是 lock address 的那几行;若 blocking_session 非空,且对应会话的 sql_opname 是 CREATE OR REPLACE PACKAGE 或 ALTER VIEW,基本就是它了。
常见陷阱:
-
sql_id可能为空——DDL 编译阶段尚未生成完整 SQL ID,此时得靠sql_opname+session_id关联v$session查sql_text - 误把
library cache: mutex X当成library cache lock——两者机制不同,不能混查 - RAC 环境下只查当前节点的 ASH,漏掉跨节点持锁会话;必须在所有节点上分别跑,或用
GV$ACTIVE_SESSION_HISTORY
为什么 CREATE OR REPLACE PACKAGE 容易卡住 library cache lock
这个操作不是原子的:Oracle 先以独占模式(mode=3)加 library cache lock 删除旧对象,再加载新版本。中间若有任何延迟(比如包体过大、依赖对象未就绪、共享池碎片),锁就一直挂着。
典型表现是多个会话在等同一个 handle address,而持锁会话的 sql_text 显示类似 CREATE OR REPLACE PACKAGE pkg_xxx AS ...,且 state 是 WAITING 或 ON CPU 但长时间不动。
实操建议:
- 查
v$db_object_cache确认该包是否已存在且locks> 0:SELECT name, type, locks FROM v$db_object_cache WHERE name = 'PKG_XXX' AND type = 'PACKAGE'; - 避免在业务高峰执行;如必须,先用
ALTER SESSION SET EVENTS '10046 trace name context forever, level 12'捕获其 parse 路径,确认是否真因依赖失效引发连锁等待 - 改用分步操作:
DROP PACKAGE后立即COMMIT,再CREATE PACKAGE,减少锁持有窗口
如何区分是 DDL 持锁还是登录失败导致的 library cache lock
Oracle 11g 的密码延迟验证(Bug 9776608)会导致大量错误登录尝试堆积 library cache lock,现象和 DDL 持锁高度相似,但本质完全不同。
快速判断依据:
- 等待会话的
username为空,或为被攻击的目标用户(如WX),且program多为oracle@host (TNS V1-V3)—— 很可能是客户端反复试错 - AWR 中
Time Model下connection management call elapsed time占 DB Time 主要部分 -
v$session_wait中p3值为5373954(mode=2)或5373955(mode=3),但sql_id和sql_opname全为空 - 查
dba_audit_session或监听日志,确认是否有大量FAILED登录记录指向同一用户
这时候别去查 DDL,该关事件:ALTER SYSTEM SET EVENT = '28401 TRACE NAME CONTEXT FOREVER, LEVEL 1' SCOPE = SPFILE;,然后重启实例。
为什么 FLUSH SHARED_POOL 在 RAC 下会让问题更糟
很多人一见 library cache lock 就想清共享池,但在 RAC 下这是高危操作。因为 ALTER SYSTEM FLUSH SHARED_POOL 是全局广播,所有节点同时清空本地 shared pool,触发海量硬解析,瞬间放大锁竞争。
真正该做的是精准清理:
- 用
DBMS_SHARED_POOL.PURGE清单个对象:EXEC DBMS_SHARED_POOL.PURGE('PKG_NAME', 'P'); - 确认对象确实在缓存中:
SELECT * FROM v$db_object_cache WHERE name = 'PKG_NAME' AND type = 'PACKAGE' AND kept = 'NO'; - 清之前先查
v$sqlarea是否有大量子游标(version_count> 100)—— 若有,优先解决绑定变量空值(Bug 8198150)或文字值问题,而不是清池
DDL 引发的锁等待,根子往往不在缓存大小,而在事务边界不清晰、依赖管理缺失、或 11g 特性(如密码延迟、ACS)与应用行为耦合太紧。盯住 ASH 里的 handle address 和 blocking_session,比盲目调参更有效。











