必须立即查v$active_session_history并限定最近5分钟,用final_blocking_session和final_blocking_instance定位RAC中真正持锁者,结合p1raw值区分mode=6真行锁与mode=4假行锁,并通过CURRENT_OBJ#/FILE#/BLOCK#/ROW#拼出被锁行ROWID。

必须立刻查 v$active_session_history,别等 AWR 报告——ASH 是唯一能抓到“未提交事务正在持锁”的实时证据。AWR 滞后、采样稀疏,等它出来时业务可能已雪崩。
查 v$active_session_history 必须加时间窗和关键过滤条件
不设时间范围直接查会扫全内存 buffer(默认约 1 小时),轻则查询卡死,重则因权限不足报 ORA-00942。只看最近 5 分钟足够定位活跃锁争用:
SAMPLE_TIME > SYSDATE - 5/1440-
session_state = 'WAITING'—— 排除 CPU 活跃但没真卡住的会话 -
event LIKE 'enq: TX%'—— 锁等待事件,不是enq: TM或library cache lock -
CURRENT_OBJ#高度集中(比如连续多行都是 12345)—— 指向同一张核心表,大概率是批处理目标表
RAC 环境下必须用 gv$active_session_history 查 final_blocking_session
在 RAC 中,blocking_session 只指向本实例的中间阻塞者,不是根因。比如 A(实例2)→ B(实例1)→ C(实例1),你查实例1 的视图,看到 B 在阻塞 C,但真正没提交的是 A——它根本不在当前实例里。
正确做法是查 gv$active_session_history,并强制带上这两个字段:
-
final_blocking_session—— 真正持锁的会话 ID -
final_blocking_instance—— 它所在的实例号,必须用SELECT instance_number FROM v$instance确认该实例真实在线 - 若
final_blocking_session为 0 或 NULL,说明它就是根阻塞源——90% 是那个UPDATE /*+ PARALLEL */ WHERE batch_id = ?后忘了COMMIT的批处理会话
区分 mode=6(真行锁)和 mode=4(假行锁)
从 v$session 查到等待事件是 enq: TX - row lock contention 后,别急着 kill。先看 p1raw 值:
- 若
p1raw = '54580006'(后两位是 06)→mode=6→ 标准行锁:A 更新某行未提交,B 等同一行 → 杀 A 或让 A 提交即可 - 若
p1raw = '54580004'(后两位是 04)→mode=4→ 很可能不是行锁,而是唯一键冲突或 ITL 不足 → 即使杀掉等待者,下一个会话仍会报ORA-00001或继续等 - 验证是否唯一键冲突:查该
sql_id对应的语句是否是INSERT INTO
用 CURRENT_ROW# 定位被锁的具体行
当 ASH 捕获到 enq: TX - row lock contention 等待时,它会记录 CURRENT_OBJ#、CURRENT_FILE#、CURRENT_BLOCK#、CURRENT_ROW#。这四个值可拼出被锁行的 ROWID:
SELECT DBMS_ROWID.ROWID_CREATE(1, CURRENT_OBJ#, CURRENT_FILE#, CURRENT_BLOCK#, CURRENT_ROW#) FROM v$active_session_history WHERE event = 'enq: TX - row lock contention' AND ROWNUM = 1;
这个 ROWID 能直接让开发确认是哪条业务记录卡住了整个流程。注意:该值仅在锁等待发生瞬间有效,事务提交后即失效。


















