应查 GV$ACTIVE_SESSION_HISTORY,因其每秒采样、含 final_blocking_instance 和 final_blocking_session 字段,可直击级联阻塞最上游持锁者,且支持 RAC 环境精准定位。

查 GV$ACTIVE_SESSION_HISTORY 而不是 V$SESSION 或 DBA_HIST_ACTIVE_SESS_HISTORY
单靠 v$session 只能看到“此刻谁在等谁”,但级联阻塞(比如 A → B → C → D)往往在采样间隙中动态演进,v$session 的 blocking_session 字段可能只链一级,且 RAC 下不带 blocking_instance 就直接丢掉跨节点环节。而 dba_hist_active_sess_history 是 10 秒采样一次、入库有延迟,死锁或级联锁常在 1–2 秒内完成,历史表大概率只留下断裂快照。
必须用 gv$active_session_history,它每秒采样(内存中保留约 1 小时),且含 final_blocking_instance 和 final_blocking_session —— 这两个字段指向**最上游的持锁者**,跳过中间转发节点,直击根源。
- 执行时务必加
inst_id或final_blocking_instance过滤,否则 RAC 下结果混杂多实例数据 - 时间窗口别拉太宽:用
sample_time > SYSDATE - 5/1440(最近 5 分钟)足够,再宽噪音剧增 - 避免全表扫:
WHERE event = 'enq: TX - row lock contention' AND final_blocking_session IS NOT NULL是最精简起点
用 final_blocking_session + final_blocking_instance 定位根阻塞会话
final_blocking_session 不是普通 blocking_session,它是 Oracle 内部递归解析后确认的“源头”,哪怕中间经过 3 层转发,这个字段也直接指向第一个没被任何人阻塞、却在持锁的会话。配合 final_blocking_instance,就能立刻知道该去哪个实例连上去查实时状态。
- 如果
final_blocking_session = 0或为空,说明根阻塞者不是普通会话,可能是后台进程(如 SMON 清理事务)、未提交的批处理作业、或已断开但事务未回滚的僵死连接 - 查到
final_blocking_instance = 2、final_blocking_session = 1058后,立刻切到实例 2 执行:SELECT sid, serial#, username, status, sql_id, event, logon_time FROM v$session WHERE sid = 1058 - 重点看
status:若为INACTIVE,说明会话已退出但事务残留;若为ACTIVE且event是SQL*Net message from client,基本可断定应用端没发 commit
结合 P1/P2 和 CURRENT_OBJ# 确认锁资源是否一致
多个会话显示同一 final_blocking_session,不代表它们真被同一个资源阻塞——得验证锁对象是否重叠。对 enq: TX - row lock contention,关键看 p1(undo segment#)和 p2(slot#):若 A 和 B 的 p1/p2 互换相等(A.p1 = B.p2 且 A.p2 = B.p1),就是同一行事务级互锁;若都指向同一个 current_obj#,说明锁集中在某张表的主键或唯一键上。
-
current_obj# = 0表示锁不在具体对象上,可能是回滚段争用,需查v$rollname和v$transaction - 别只信
sql_id:不同 SQL 更新同一行主键,也会触发相同 TX 锁,sql_id可能完全不同 - 用
SELECT object_name FROM dba_objects WHERE object_id = ¤t_obj#快速反查表名,比翻 SQL 文本更快
别跳过实时验证:立刻查 GV$TRANSACTION 和 V$SESSION
ASH 告诉你“谁在等”,但不保证“谁还在持”。Oracle 自动 rollback 死锁牺牲者后,final_blocking_session 可能在后续采样中清空,ASH 里只剩等待方记录。所以一旦从 ASH 锁定根会话,必须立刻连对应实例查真实状态。
- 执行:
SELECT start_date, used_ublk, xidusn, xidslot, xidsqn FROM gv$transaction t JOIN gv$session s ON t.ses_addr = s.saddr WHERE s.sid = &sid - 若查不到记录,说明事务已结束;若
used_ublk > 1000且start_date是 10 分钟前,大概率是长事务未提交 - 再补一句:
SELECT sql_text FROM v$sql WHERE sql_id = '&sql_id',确认是不是UPDATE ... WHERE id = ?类语句没配 commit
真正容易被忽略的是:final_blocking_instance 对应的实例可能刚好在你查询时重启过,gv$session 查不到那个 SID —— 这时候得回头翻 gv$active_session_history 里该会话最近 10 条记录的 sql_id 和 program,靠应用特征反推是谁。


















