必须立即查gv$active_session_history并限定最近5分钟,用final_blocking_session和final_blocking_instance定位RAC中真正持锁者,同时备份ASH、gv$transaction及gv$session数据以防丢失。

直接查 v$active_session_history,别等 AWR 报告
ASH 是唯一能抓到“大事务未提交”正在持锁的实时证据。AWR 报告滞后、采样稀疏,等它出来时,事务可能已回滚或业务已雪崩。必须立刻查 v$active_session_history,且只看最近 5 分钟:时间窗不设,SAMPLE_TIME > SYSDATE - 5/1440 就会扫全内存 buffer(默认约 1 小时),轻则查询卡死,重则因权限不足报 ORA-00942。
关键过滤条件缺一不可:
-
session_state = 'WAITING'—— 排除 CPU 活跃但没真卡住的会话 -
event LIKE 'enq: TX%'—— 锁等待事件,不是enq: TM或library cache lock -
CURRENT_OBJ#高度集中(比如连续多行都是 12345)—— 指向同一张核心表,大概率是批处理目标表 - 搭配
sql_id频次统计:同一sql_id在 5 分钟内出现 >20 次样本,基本锁定是长事务主 SQL
识别“持锁者”不能只看 blocking_session
在 RAC 环境下,blocking_session 只指向本实例的中间阻塞者,不是根因。比如 A(实例2)→ B(实例1)→ C(实例1),你查实例1 的 v$active_session_history,看到 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 ... VALUES (...),且current_obj#指向唯一索引所在表
备份 ASH 快照防丢失,别只依赖内存
v$active_session_history 是内存结构,重启或 ASH buffer 溢出就会丢数据。一旦发现批量 enq: TX 等待,立刻执行:
CREATE TABLE ASH_BAK_<date> AS SELECT * FROM gv$active_session_history WHERE sample_time > SYSDATE - 5/1440;
这个动作要快,因为:
- ASH buffer 默认保留约 1 小时,但高负载下可能被提前覆盖
- 大事务持续越久,后续新采样越容易把关键前几秒的样本挤掉
-
gv$active_session_history跨实例,单节点备份会漏掉 RAC 中真正的持锁实例
最易被忽略的一点:很多人只备份 v$active_session_history,却忘了 gv$transaction 和 gv$session 的关联状态——没有 xidusn/xidslot/xidsqn,就无法反查谁在哪个事务里卡了多久。


















