必须用gv$active_session_history:它每秒采样、内存保留约1小时、含final_blocking_session和final_blocking_instance字段,能直击真正持锁者,而v$session仅链一级阻塞、dba_hist_active_sess_history采样稀疏易漏峰。

直接查 gv$active_session_history,5 分钟内高频出现同一 sql_id + enq: TX% 等待 + CURRENT_OBJ# 高度集中,基本就是那个没提交的批量更新主凶。
为什么不能只查 v$session 或 dba_hist_active_sess_history
单靠 v$session 只能看到“此刻谁在等谁”,但批量更新锁表往往伴随级联阻塞(A→B→C→D),blocking_session 字段最多链一级,且 RAC 下不带 blocking_instance 就直接丢掉跨节点环节;dba_hist_active_sess_history 是 10 秒采样一次、入库有延迟,而批量更新引发的锁排队常在几秒内爆发并持续数分钟,历史表大概率只留下断裂快照或完全错过峰值。
必须用 gv$active_session_history:它每秒采样、内存保留约 1 小时、含 final_blocking_session 和 final_blocking_instance 字段,能跳过中间转发节点,直击真正持锁者。
怎么写查询语句才能“一键抓出主凶”
执行以下语句(注意替换时间窗口和实例号):
SELECT sql_id, CURRENT_OBJ#, COUNT(*) cnt FROM gv$active_session_history WHERE sample_time > SYSDATE - 5/1440 AND session_state = 'WAITING' AND event LIKE 'enq: TX%' AND CURRENT_OBJ# IS NOT NULL GROUP BY sql_id, CURRENT_OBJ# HAVING COUNT(*) > 20 ORDER BY cnt DESC;
-
sample_time > SYSDATE - 5/1440:限定最近 5 分钟,太宽会扫全内存 buffer,轻则卡死,重则因权限不足报ORA-00942 -
session_state = 'WAITING':排除 CPU 活跃但没真卡住的会话 -
event LIKE 'enq: TX%':只抓 TX 类锁等待,不是enq: TM或library cache lock -
HAVING COUNT(*) > 20:同一sql_id在 5 分钟内被采样超 20 次,基本锁定是长事务主 SQL
怎么确认是不是“真行锁”而不是假报警
查到可疑 sql_id 后,别急着 kill,先看它的等待样本里 p1raw 值:
- 若
p1raw = '54580006'(后两位是06)→mode=6→ 标准行锁:A 更新某行未提交,B 等同一行 → 杀 A 或让 A 提交即可 - 若
p1raw = '54580004'(后两位是04)→mode=4→ 很可能不是行锁,而是唯一键冲突或 ITL 不足 → 即使杀掉等待者,下一个会话仍会报ORA-00001或继续等
验证是否唯一键冲突:查该 sql_id 对应语句是否是 INSERT INTO;验证是否 ITL 不足:看 CURRENT_OBJ# 对应表的 INITRANS 是否偏低、并发 DML 是否密集。
怎么定位到真正没提交的会话(尤其 RAC 环境)
拿到高频 sql_id 后,执行:
SELECT DISTINCT final_blocking_session, final_blocking_instance FROM gv$active_session_history WHERE sample_time > SYSDATE - 5/1440 AND sql_id = '&sql_id' AND event LIKE 'enq: TX%' AND final_blocking_session IS NOT NULL;
关键点:
-
final_blocking_session不是普通blocking_session,它是 Oracle 内部递归解析后确认的“源头”,哪怕中间经过 3 层转发,这个字段也直接指向第一个没被任何人阻塞、却在持锁的会话 -
final_blocking_instance必须配合使用——查到final_blocking_instance = 2后,立刻切到实例 2 连上去查:SELECT sid, serial#, username, status, sql_id, event FROM v$session WHERE sid = &final_blocking_session - 如果
final_blocking_session = 0或为空,说明根阻塞者不是普通会话,可能是后台进程、未提交的批处理作业、或已断开但事务未回滚的僵死连接
最易忽略的是:status = INACTIVE 的会话。它可能早已退出,但事务残留未清理,此时 v$transaction.used_ublk 往往很高(>10000 表示已用超 80MB undo),必须连过去查真实状态,不能只看当前活跃会话列表。











