必须用event='enq: tx - row lock contention'+session_state='waiting'+wait_class='concurrency'三条件锁定真实等待,再靠final_blocking_session穿透级联链;blocking_session在ash中仅为采样瞬间快照,锁持有短于1秒或持锁会话在采样间隙提交/回滚即为空,漏报率高。

直接结论:别只查 blocking_session,它在 ASH 里漏报率高;必须用 event = 'enq: TX - row lock contention' + session_state = 'WAITING' + wait_class = 'Concurrency' 三条件锁定真实等待,再靠 final_blocking_session 穿透级联链。
为什么 blocking_session 在 ASH 里经常为空
ASH 的 blocking_session 是采样瞬间快照,不是实时状态。锁持有时间短于 1 秒(比如 300ms)、持锁会话在两次采样之间提交或回滚、甚至只是网络抖动导致采样丢失,都会让它变空。你看到大量 enq: TX% 等待但 blocking_session IS NULL,不等于没阻塞——大概率是漏采,或者根本不是行锁,而是 library cache lock 伪装出来的。
实操建议:
- 永远不要单独依赖
blocking_session字段做判断 - 把
event、session_state、wait_class三者组合当“铁三角”,缺一不可 - 如果这三者成立但
blocking_session为空,优先查final_blocking_session(Oracle 12c+) - RAC 环境下,
blocking_session可能指向本节点中间会话,而真正持锁者在另一实例上
怎么用 final_blocking_session 找到根因会话
final_blocking_session 和 final_blocking_instance 是 Oracle 12c+ 推荐字段,它跳过 A→B→C 中间层,直指源头。但注意:它只在 ASH 里存在,且必须配合时间过滤才可信。
实操建议:
- 先查 ASH:
SELECT session_id, final_blocking_session, final_blocking_instance, sql_id FROM v$active_session_history WHERE event = 'enq: TX - row lock contention' AND sample_time > SYSDATE - 3/1440 AND session_state = 'WAITING' - 拿到
final_blocking_session值后,立刻去v$session查实时状态:SELECT sid, serial#, username, status, event, sql_id FROM v$session WHERE sid = <value></value> - 若查不到该 SID,说明它已提交/回滚/断连;这时要转向
dba_hist_active_sess_history或结合v$transaction看长事务 - RAC 下必须校验
final_blocking_instance是否有效:SELECT instance_number FROM v$instance,避免查错节点
如何还原真实阻塞链,而不是拼凑伪链
ASH 是滚动内存表,sample_id 严格递增,每秒一条。用 CONNECT BY PRIOR blocking_session = session_id 会强行把不同时刻的会话连起来,比如上午 9:45 的会话 A 和下午 3:20 的会话 B 错误关联。
实操建议:
- 聚焦单一会话:先锁定一个高频等待的
session_id和session_serial# - 查它前后几秒样本:
SAMPLE_ID BETWEEN X-2 AND X+2,观察事件是否出现enq: TX - row lock contention→db file sequential read→cursor: pin S wait on X这类连锁 - 多个会话都指向同一
sql_id?优先检查该 SQL 是否操作主键/唯一索引列、是否有FOR UPDATE、事务是否未提交 - 想展开完整链,必须物化中间结果:先
WITH ash_data AS (SELECT ... FROM v$active_session_history WHERE ...),再用递归 CTE,且校验inst_id、session_serial#、时间连续性
容易被忽略的 RAC 和瞬时等待陷阱
RAC 下跨实例阻塞不会出现在本地 v$session,而 library cache pin 类等待的 blocking_session 几乎总是空——因为它是共享池结构竞争,不是传统会话阻塞。
实操建议:
- RAC 必须用
gv$active_session_history,单查v$视图会漏掉 GC 相关阻塞(如gc current block 2-way) - 遇到
library cache pin,别盯blocking_session,改用p1raw关联x$kglob和x$kglpn定位被锁对象 - 瞬时等待(v$session_wait 里大概率消失,但 ASH 仍可能捕获;所以初筛优先看 ASH,确认后再切到实时视图验证
-
final_blocking_session为空但系统持续 Hang?不是锁问题,是资源耗尽型:立刻查v$system_event里latch: shared pool、library cache: mutex X的time_waited_micro
最常被跳过的动作是:查到 final_blocking_session 后,不立刻切到 v$session 看它此刻是否还在等、有没有新阻塞、SQL 是否真在跑——快照和实时视图的时间差,就是根因定位成败的关键分界线。











