直接查v$active_session_history可秒级定位行锁争用SQL,ASH是唯一能捕获刚发生TX锁争用的视图,结合blocking_session和p1/p2可精准追溯持有者与具体行。
直接查v$active_session_history定位争用SQL
别等awr报告,ash是唯一能秒级捕获刚发生的行锁争用的视图。只要enq: tx - row lock contention在最近一小时内出现过,就能查到源头sql。
关键不是“有没有”,而是“哪几条sql_id反复撞同一行”。执行以下语句(注意时间过滤):
SELECT sql_id, current_obj#, COUNT(*) cnt FROM v$active_session_history WHERE sample_time > SYSDATE - 1/24 AND event = 'enq: TX - row lock contention' AND session_type = 'FOREGROUND' GROUP BY sql_id, current_obj# ORDER BY cnt DESC;
- 必须加
sample_time > SYSDATE - 1/24:ASH内存默认只保留约1小时,不加会扫全库历史,慢且结果不可靠 -
current_obj#是对象编号,不是object_id——它可能来自已重建的表,后续构造ROWID前得先校验 - 如果
sql_id为空,说明可能是PL/SQL匿名块或解析失败,此时要结合program和module字段人工判断来源
用blocking_session反向揪出“元凶”会话
争用SQL只是“被堵者”,真正卡住它的往往是早先未提交的事务。ASH里的blocking_session字段就是线索,但它只在采样瞬间有效,不能只查一次就下结论。
执行这个查询,把等待方和持有方一起捞出来:
SELECT sample_time, sid, blocking_session, sql_id, event, p1, p2
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/(24*60)
AND event = 'enq: TX - row lock contention'
AND (blocking_session IS NOT NULL OR sid IN (
SELECT blocking_session
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/(24*60) AND blocking_session IS NOT NULL
));
-
blocking_session指向的会话可能早已提交退出,所以得看它在ASH里是否也留下过enq: TX记录——如果没留,大概率是“瞬时持锁”,不用深挖 - 若发现A的
blocking_session= B,而B的blocking_session= A,就是死锁雏形,立刻查这两会话的sql_text和事务起始时间 -
p1和p2是TX锁的关键:p1=undo segment#,p2=slot#,两个会话若互为p1/p2,基本可确认互锁
构造ROWID定位具体行时最容易错的三件事
拿到current_file#、current_block#、current_row#后,用DBMS_ROWID.ROWID_CREATE生成ROWID看似简单,但实际90%的失败都源于参数无效。
-
current_file#和current_block#可能来自已归档或被覆盖的ASH样本,必须加WHERE current_file# > 0 AND current_block# > 0 AND current_row# >= 0过滤 - 对象可能已被重建:
SELECT owner, object_name FROM dba_objects WHERE object_id = :current_obj#能查到表名,但data_object_id可能已变——DBMS_ROWID.ROWID_CREATE必须用当前data_object_id,不是object_id -
DBMS_ROWID.ROWID_CREATE(1, :data_object_id, :file#, :block#, :row#)四个参数必须全是正整数,任一为NULL或负数,函数就返回NULL,不会报错
为什么v$session_wait查不到,但ASH能查到
v$session_wait只反映会话“当前”等待状态,而enq: TX - row lock contention平均持续几十毫秒——你连上去时,它很可能已经结束。ASH的优势在于每秒采样,只要争用发生过,就有概率被捕获。
v$session_wait对TX锁只暴露p1(undo segment#)和p2(slot#),没法反推具体行;而ASH强制记录CURRENT_FILE#、CURRENT_BLOCK#、CURRENT_ROW#这组物理地址三元组。
-
CURRENT_ROW#是块内行号(0-based),不是逻辑行号,SELECT * FROM tab WHERE ROWID = :rid才能看到真实数据 - ASH中
sql_id可能为空,但program和machine字段往往还在,这对定位应用端代码位置更直接 - 如果
STATISTICS_LEVEL设为BASIC,ASH会被禁用,查任何内容都是空——先确认SELECT value FROM v$parameter WHERE name = 'statistics_level'











