ASH返回空主因是时间窗口不当、内存缓冲区覆盖或未加sample_time过滤;其每秒采样非空闲会话,SGA中仅保留约1小时数据,需用WHERE sample_time > SYSDATE - 1/1440限定最近1分钟。
查不到最近的活跃会话,大概率不是ash没数据,而是你没用对时间窗口或内存缓冲区已满被覆盖。
查 V$ACTIVE_SESSION_HISTORY 为什么返回空?
ASH 默认每秒采样一次,但只在 SGA 的内存缓冲区中保留约 1 小时(实际受 _ash_size 和负载影响),且仅记录非空闲状态的会话。刚卡顿完立刻查却没结果,常见原因有三个:
- 采样还没来得及捕获——比如卡顿只持续了 200ms,而 MMNL 进程刚好错过那一秒
- 会话已退出,对应采样记录被新数据循环覆盖
- 查询没加时间过滤,
WHERE sample_time > SYSDATE - 1/1440(过去 1 分钟)必须加上,否则默认可能扫全表但命中率极低
别依赖 SELECT * FROM v$active_session_history 直接跑,先确认缓冲区是否真有数据:
SELECT * FROM v$sgastat WHERE name LIKE '%ASH%';
如果 bytes 为 0 或极小(如
如何确认 ASH 内存分配是否足够?
_ash_size 是控制内存缓冲区大小的隐含参数,11g 起默认 4MB,12c 仍沿用该基线。对于高并发实例,4MB 很快就溢出,导致采样丢失。检查当前值:
SHOW PARAMETER _ash_size
若返回为空或数值偏低(如 4194304 = 4MB),且你常遇到“刚卡就查不到”的情况,需考虑扩容。但注意:
- 修改后必须重启实例才生效:
ALTER SYSTEM SET "_ash_size"=524288000 SCOPE=SPFILE;(设为 500MB) - 过大的
_ash_size会挤占其他 SGA 组件空间,尤其当sga_max_size未同步调高时,可能引发启动失败 - 优先验证是否真缺内存:对比
v$sgastat中 ASH buffers 的bytes和v$active_session_history中最大sample_time时间差,若差值远小于 60 分钟,基本可判定缓冲区撑不住
ASH 查到大量 enq: TX - row lock contention 却找不到阻塞源?
ASH 记录的是“正在等”的瞬间,不是“正在锁”的状态。所以看到一堆 enq: TX - row lock contention,不代表你能直接从 blocking_session 列拿到答案——它经常为空。
真正有用的是三列组合:
-
session_id:被阻塞的会话 -
blocking_session:直接阻塞者(11g+ 可用,但不稳定) -
final_blocking_session:根因会话(12c+ 才稳定输出,必须用)
执行这个查询定位链路:
SELECT session_id, blocking_session, final_blocking_session, sql_id, event, p1text, p1 FROM v$active_session_history WHERE event = 'enq: TX - row lock contention' AND sample_time > SYSDATE - 1/720 ORDER BY sample_time DESC;
若 blocking_session 为空但 final_blocking_session 有值,说明存在级联阻塞(A→B→C),此时要顺藤摸瓜去查 C 在 v$session 中的状态;若两者都为空,大概率是锁持有时间太短(v$session 实时抓取 + v$sql 关联 sql_id 排查。
为什么有些 TX 锁查不到 SQL_ID?
不是所有 TX 等待都来自 DML。DDL 操作(如 ALTER TABLE ... MOVE、TRUNCATE)也会隐式申请 TX 锁,且不走常规 SQL 解析路径,因此 sql_id 为 NULL。
这时不能只盯 sql_id,得看上下文字段:
-
machine和program:识别是哪个应用服务器或后台进程发起的 -
module和action:很多应用会设置这些字段,能快速定位业务模块 -
client_identifier:若应用启用了 DBMS_SESSION.SET_IDENTIFIER,这里会有业务标识
特别注意:某些运维脚本或备份工具(如 RMAN)在执行表空间迁移或数据泵导出时,也会触发 TX 锁争用,且往往没有有意义的 sql_id。


















