看到大量会话卡在enq: tx - row lock contention即可确认是行锁争用;需用ash回溯过去几分钟采样点定位根阻塞者和热点sql,避免依赖瞬时视图v$session。

怎么快速确认是行锁而不是其他锁?
看到大量会话卡在 enq: TX - row lock contention,基本不用再猜——这就是行锁争用。别去查 latch: shared pool 或 enq: TM - contention,它们和行锁无关。ASH里这个事件出现频率一旦超过 70%,说明问题根因就在这里,直接聚焦它。
为什么不能只看 v$session?
v$session 只反映“此刻”的阻塞关系,但真实故障常发生在 2–3 分钟前。比如会话 A 在 05:46:12 提交了事务,但之前它持锁导致的连锁阻塞,在 05:46:20 就已消散,v$session 此时查不到 blocking_session,你会误判为“没阻塞”。必须用 dba_hist_active_sess_history 回溯过去几分钟的采样点。
容易踩的坑:
- 直接 SELECT * FROM dba_hist_active_sess_history,全表扫描卡死且权限可能不足
- 时间范围跨时区:务必用数据库所在时区写 WHERE sample_time BETWEEN TIMESTAMP '2026-07-27 05:45:00' AND TIMESTAMP '2026-07-27 05:48:00'
- 忘记加 sample_id = PRIOR sample_id - 1 就用 CONNECT BY 追链,结果把不同时刻的会话强行连成一条“幽灵阻塞链”
如何定位根阻塞者和热点 SQL?
查出的 blocking_session 如果为 0 或空,说明它是源头;如果反复出现同一 sql_id,优先检查它的执行计划和事务状态。
实操建议:
- 先聚合最近 1 小时内最常出现的阻塞 SQL:SELECT sql_id, COUNT(*) cnt FROM dba_hist_active_sess_history WHERE event = 'enq: TX - row lock contention' AND sample_time > SYSDATE - 1/24 GROUP BY sql_id ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY
- 对每个 top sql_id,查 v$sql 看 executions 和 elapsed_time:若 executions 很低但 elapsed_time 高,大概率是慢 SQL 持锁时间长
- 再查 SELECT * FROM v$sql WHERE sql_id = 'xxx',重点看是否含 FOR UPDATE、未提交的 UPDATE/DELETE,或是否在应用层开启长事务
阻塞链里 current_obj# = 0 怎么办?
current_obj# = 0 不代表没对象,可能是刚进入逻辑读、还没绑定段名,这时更要盯紧 current_file# 和 current_block#。尤其在 RAC 环境下,LCK0 进程协调时也常出现该现象。
真正关键的是反推物理位置:
- 用 SELECT current_file#, current_block#, COUNT(*) cnt FROM v$active_session_history WHERE event = 'enq: TX - row lock contention' AND sample_time > SYSDATE - 1/48 GROUP BY current_file#, current_block# ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY
- 再用 DBA_EXTENTS 匹配:条件必须是 &BLOCK_ID BETWEEN block_id AND block_id + blocks - 1,不是等值查询
- 查不到结果?说明块属于系统段(回滚段、UNDO、临时段),此时转向 v$rollstat 或 v$tempseg_usage
真实阻塞往往藏在“已结束但影响尚存”的窗口期里,ASH 的采样粒度和保留时长(Oracle 默认 1 小时)决定了你必须卡准时间点,否则看到的只是残影。











