blocking_session字段常为空,因其仅记录采样瞬间的阻塞者,锁持有时间短于1秒或持锁会话在采样间隙提交/回滚即导致漏报;应结合event、session_state、wait_class三条件确认真实行锁等待,并优先使用12c+的final_blocking_session定位根因会话。
blocking_session 字段为什么经常为空?
ash 里的 blocking_session 不是“当前谁在锁我”,而是“采样那一毫秒,谁刚好被记录为阻塞源”。它非常脆弱:如果持锁会话在两次采样之间提交、回滚或完成,这个字段就变成 null。更常见的是,锁持有时间短于 1 秒(比如 300ms),根本没被采到。
别只盯 blocking_session —— 它漏报率高。真正可靠的是组合判断:event = 'enq: TX - row lock contention' + session_state = 'WAITING' + wait_class = 'Concurrency'。这三个同时成立,基本能确认是真实行锁等待。
- 查的时候必须加时间过滤,例如
sample_time > SYSDATE - 1/24(最近一小时),否则默认扫全历史,慢且干扰多 - 如果发现大量等待但
blocking_session总为空,优先怀疑是不是硬解析争用(library cache lock或游标共享冲突),而不是业务数据行锁 - 12c+ 环境下,
final_blocking_session比blocking_session更值得信任,它能穿透 A→B→C 的级联阻塞链
怎么用 blocking_session 连出完整阻塞链?
单条 ASH 记录只能看到“当前等待者”和“当时记录的阻塞者”,没法自动展开整条链。但你可以用 final_blocking_session 当起点,再反向查它是否也在等别人。
最实用的做法是分两步走:
- 先找出所有等待行锁的会话:
SELECT session_id, final_blocking_session, sql_id FROM v$active_session_history WHERE event = 'enq: TX - row lock contention' AND sample_time > SYSDATE - 1/1440 - 再对返回的
final_blocking_session值,去查v$session:SELECT sid, serial#, sql_id, status, event, blocking_session FROM v$session WHERE sid IN (/* 上一步结果 */) - 若第二步里某些会话的
blocking_session非空,说明还有上游;继续递归查,直到blocking_session为空或查不到记录为止
注意:v$session 是实时视图,查出来的是“此刻”的状态;而 ASH 是快照,两者时间点不同,不能直接 JOIN 匹配,得靠人工串联。
current_obj# 和 blocking_session 对不上怎么办?
常见现象:ASH 显示某 SQL 在表 X 上争用(current_obj# = 12345),但 blocking_session 查出来执行的是完全无关的语句,甚至 sql_id 是空的。
这通常不是数据错乱,而是因为:
-
current_obj#来自采样时刻的物理上下文,指向的是“正在访问的对象”,但它不保证该会话当前 SQL 就操作这张表(比如 PL/SQL 中调用了多个过程) -
blocking_session只记录 SID,不带 SQL 上下文;如果那个会话刚执行完 DML 就 idle 了,v$session.sql_id可能已清空 - 对象可能已被重建:
dba_objects.object_id没变,但data_object_id变了,导致用current_obj#构造 ROWID 失败
验证方法:SELECT owner, object_name, object_type FROM dba_objects WHERE object_id = 12345。如果查不到,说明对象已删;如果查到了但 data_object_id 和你从 ASH 里拿到的不一致,那 DBMS_ROWID.ROWID_CREATE 就会返回 NULL。
为什么 final_blocking_session 比 blocking_session 更有用?
final_blocking_session 是 Oracle 12c 引入的字段,它的设计目标就是解决传统 blocking_session “只看一层”的缺陷。它不是简单记录直接阻塞者,而是 Oracle 内部通过锁队列扫描后推导出的“根因会话”。
比如 A 锁住某行 → B 等 A → C 等 B,那么:
-
blocking_session在 B 的记录里是 A,在 C 的记录里是 B -
final_blocking_session在 B 和 C 的记录里都是 A
所以,排查时优先用 final_blocking_session 做聚合:GROUP BY final_blocking_session, current_obj#,能直接定位到“哪个会话、在哪张表上制造了最多等待”。但要注意:这个字段在 12c 初期版本(如 12.1.0.1)不稳定,建议确认数据库补丁级别;另外它只存在于 v$active_session_history,v$session 里没有对应字段。
最易被忽略的一点:即使 final_blocking_session 有值,也不能假设它“还在运行”——它可能早已提交,只是 ASH 快照恰好捕获了它持锁的瞬间。所以查到后,一定要立刻去 v$session 确认该 SID 是否 still active、是否有未提交事务。


















