ASH是唯一能回溯死锁前几秒真实阻塞链的手段,因DBA_HIST_ACTIVE_SESS_HISTORY采样滞后且字段不稳定,需先用V$ACTIVE_SESSION_HISTORY定位“黄金5分钟”内互指会话,再以精确时间锚点查历史表归因。
ash 不能直接“解决”死锁,但它是唯一能回溯死锁发生前几秒真实阻塞链的手段;dba_hist_active_sess_history 是历史证据库,不是实时诊断入口——查它之前,必须先用 v$active_session_history 锁定时间窗口和会话对。
为什么 DBA_HIST_ACTIVE_SESS_HISTORY 查不到刚发生的死锁
Oracle 默认只把 ASH 内存采样(v$active_session_history)每 10 秒批量刷入历史表 dba_hist_active_sess_history,且保留策略通常为 1 小时。这意味着:
- 死锁持续时间常小于 1 秒,而历史表采样点可能刚好错过关键秒(比如死锁发生在 14:22:37,但最近入库的是 14:22:30 和 14:22:40 的样本)
-
dba_hist_active_sess_history中blocking_session字段在 19c 以前版本不稳定,部分采样行该值为空或为 0,导致互指关系断裂 - 一旦 Oracle 自动回滚牺牲者(victim),
blocking_session在后续采样中会被清空,历史表里只剩“单边等待”快照,看不出环
必须先用 V$ACTIVE_SESSION_HISTORY 定位“黄金 5 分钟”
死锁发生后,立刻执行以下查询(别等 AWR 报告):
SELECT sample_time, session_id, blocking_session, blocking_session_serial#,
event, p1, p2, sql_id, current_obj#
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/1440 -- 最近 5 分钟
AND event LIKE 'enq: TX%'
AND (blocking_session IS NOT NULL OR session_id IN (
SELECT blocking_session
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/1440 AND blocking_session IS NOT NULL
))
ORDER BY sample_time DESC;关键动作:
- 看到某两个会话 A 和 B 在同一
sample_time下互相指向对方的blocking_session,就是死锁链实锤 - 确认
event是enq: TX - row lock contention而非enq: TX - allocate ITL entry,后者是 ITL 不足,不是真死锁 - 记下这对会话的
session_id和精确到秒的sample_time,这是后续查历史表的唯一可靠锚点
用 DBA_HIST_ACTIVE_SESS_HISTORY 做归因而非定位
历史表真正价值在于交叉验证和影响范围分析,不是找“谁卡住了谁”。查的时候必须带两个硬条件:
- 时间范围严格限定在刚才从
v$active_session_history拿到的sample_time ± 10 秒内,写成:sample_time BETWEEN TIMESTAMP '2026-05-14 14:22:35' AND TIMESTAMP '2026-05-14 14:22:45' - 加
AND session_state = 'WAITING',过滤掉已释放锁但被采样到的“幽灵持有者” - 聚合时按
(session_id, blocking_session)分组计数,出现频次 ≥ 3 的组合才值得深挖——单次采样不可信
这时再关联 dba_hist_sqltext 查 SQL,重点看:
- 两条互锁 SQL 是否操作同一张表、同一索引字段(比如都走
idx_order_user_id) - 是否有隐式类型转换(
user_id = '123'vsuser_id = 123),导致索引失效、锁范围扩大 - 是否含
FOR UPDATE NOWAIT或未提交的UPDATE,这是典型元凶
容易被忽略的兼容性坑:19c 的 final_blocking_session
Oracle 19c 引入了 final_blocking_session 字段,它比 blocking_session 更稳定,能跨采样点追踪根阻塞者。但要注意:
- 该字段仅在
v$active_session_history中可用,dba_hist_active_sess_history中多数版本仍为空或无效 - 查
final_blocking_session时,必须同时检查final_blocking_session_serial#,两者缺一不可,单独一个为 NULL 就代表链路中断 - 如果发现
final_blocking_session = 0,说明阻塞源头是后台进程(如 SMON 清理事务),不是应用会话——这种死锁需查 alert.log 和 trace 文件,ASH 只能辅助佐证
最麻烦的情况是:ASH 里能拼出 A→B→A 环,但 v$lock 和 v$transaction 里什么也查不到。这往往意味着死锁已被 Oracle 清理,而你看到的只是残留采样——此时别再纠结“谁持锁”,直接盯住那两条 SQL 的执行路径和事务边界。


















