必须用GV$ACTIVE_SESSION_HISTORY查等待链,因其每秒采样且含final_blocking_instance和final_blocking_session字段,可直击A→B→C→D级联阻塞最上游持锁者,而V$SESSION仅显示一级阻塞且RAC下跨实例信息为空。

查等待链必须用 GV$ACTIVE_SESSION_HISTORY 而不是 V$SESSION
单靠 V$SESSION 只能看到一级阻塞(A → B),但真实等待链常是 A → B → C → D,且 RAC 下跨实例的 blocking_session 字段在单实例视图里直接为空。而 GV$ACTIVE_SESSION_HISTORY 每秒采样、带 final_blocking_instance 和 final_blocking_session,能跳过中间转发节点,直指最上游持锁者。RAC 环境下不加 GV$ 前缀,等于主动放弃一半线索。
WHERE 条件必须同时满足四个硬约束
漏掉任意一个,结果就不可信:
-
event IN ('enq: TX - row lock contention', 'library cache lock', 'enq: TM - contention')—— 只筛典型锁类事件,避免把 I/O 或空闲等待混进来 -
final_blocking_session IS NOT NULL—— 这是定位根因的关键开关,blocking_session可能只链一级,它才是 Oracle 内部递归解析后的源头 -
sample_time > SYSDATE - 5/1440(最近 5 分钟)—— 超过这个窗口,内存中 ASH 缓冲区大概率已覆盖,历史表DBA_HIST_ACTIVE_SESS_HISTORY是 10 秒一采,死锁或级联锁常在 1–2 秒内完成,基本抓不到 -
session_state = 'WAITING'—— 排除 ON CPU 样本干扰,确保你查的是真正在等资源的会话
如何验证是否真形成循环等待链
ASH 不会标出“这是死锁”,但你能从同一 sample_time 的多行数据里人工拼出环。关键交叉比对点:
- 找两行及以上记录,
sid和blocking_session互指:A 行的blocking_session = B.sid,B 行的blocking_session = A.sid - 双方
event都是enq: TX - row lock contention - 检查
p1(undo segment#)和p2(slot#):若 A.p1 = B.p2 且 B.p1 = A.p2,基本可确认 TX 锁互持 - 别只看
sql_id是否相同——绑定变量不同会导致sql_id分裂,优先比对sql_opname和current_obj#
RAC 下查到 final_blocking_instance = 2 后该怎么做
拿到 final_blocking_instance 和 final_blocking_session 后,不能只查当前实例:
- 立刻连上对应实例(比如
final_blocking_instance = 2),执行:SELECT sid, serial#, username, status, sql_id, event, logon_time FROM v$session WHERE sid = &final_blocking_session;
- 重点看
status:若为INACTIVE,说明会话已退出但事务未提交;若为ACTIVE且event = 'SQL*Net message from client',基本锁定是应用端没发COMMIT - 如果
final_blocking_session = 0,说明根阻塞者不是普通会话——可能是 SMON、未提交批处理、或已断连但事务残留的僵死连接,这时得查v$transaction和v$session_longops
最易被忽略的一点:final_blocking_session 字段在 Oracle 11g 中仅当启用 DIAGNOSTIC_PACK 才可用,没开这个选项时字段值恒为 NULL,此时只能退回到多层 blocking_session 关联查询,但准确率大幅下降。


















