ASH不存储执行计划,仅记录SQL_ID、sql_plan_hash_value等元信息;需结合DBMS_XPLAN.DISPLAY_CURSOR等工具获取实际执行步骤及耗时。

ASH里查不到SQL Execution Plan,别白费劲
ASH(Active Session History)本身不存储执行计划,只记录每秒采样的活动会话状态、等待事件、绑定变量值、SQL_ID 和当前执行的 sql_id、sql_plan_hash_value 等元信息。你无法直接从 V$ACTIVE_SESSION_HISTORY 里拿到执行计划树或操作符详情——那是 DBMS_XPLAN.DISPLAY_CURSOR 或 AWR 的活儿。
想“监控步骤级延迟”,本质是把 ASH 的高频率采样数据,和某个具体执行计划的物理操作节点对齐。这需要两层映射:先定位出问题 SQL 的 sql_id 和实际运行中的 sql_plan_hash_value,再用它们去查该计划的实际执行统计(ALLSTATS LAST)。
用ASH定位正在慢跑的SQL及其plan_hash_value
核心思路是:ASH能告诉你“此刻谁在卡”,但得靠它引路,跳转到真正带步骤耗时的视图。常用组合查询如下:
SELECT sql_id, sql_plan_hash_value, event, COUNT(*) cnt FROM v$active_session_history WHERE sample_time > SYSDATE - 1/24 -- 近1小时 AND session_state = 'WAITING' AND event LIKE 'db file sequential read%' GROUP BY sql_id, sql_plan_hash_value, event ORDER BY cnt DESC;
注意点:
-
sql_plan_hash_value是计划的指纹,同一sql_id可能有多个不同 hash 值(计划漂移),必须一起抓 - 别只看
ON CPU;db file sequential read、direct path read、enq: TX - row lock contention这些才是步骤级延迟的线索 - 如果
sql_plan_hash_value为 0,说明该次执行还没生成完整计划(比如解析阶段就卡住了)
用DISPLAY_CURSOR关联ASH采样与真实执行步骤
拿到 sql_id 和 sql_plan_hash_value 后,下一步是查这个计划最近一次执行的详细步骤耗时:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR( '<sql_id>', NULL, 'ALLSTATS LAST +PEEKED_BINDS' ));
关键字段解释:
-
STARTS:该操作被执行了多少次(比如嵌套循环内表被驱动多少轮) -
E-RowsvsA-Rows:预估 vs 实际返回行数,严重偏差往往意味着统计信息过期或谓词失效 -
Buffers/Reads:逻辑读 / 物理读次数,直接对应 I/O 延迟来源 -
Time列(需开启STATISTICS_LEVEL=ALL):每个操作的真实耗时,单位是秒,这才是“步骤级延迟”的直接证据
⚠️ 容易踩的坑:DISPLAY_CURSOR 默认只显示最近一次执行的统计,如果 SQL 已结束且没被缓存,就查不到 A-Rows 和 Time;此时必须依赖 AWR 快照(DISPLAY_AWR),但会丢失秒级精度。
为什么不能只靠ASH做步骤级归因
ASH 的采样是随机快照,不是全链路追踪。一个耗时 500ms 的 TABLE ACCESS FULL 操作,ASH 可能只在其中某 1~2 个采样点捕获到它,无法还原该操作内部的 buffer pin、latch 等子阶段延迟。真正的步骤级延迟归因,必须满足:
- SQL 执行时已开启
STATISTICS_LEVEL=ALL(否则DISPLAY_CURSOR的Time列为空) - 目标 SQL 尚未从共享池淘汰(否则
DISPLAY_CURSOR返回空或只有预估计划) - 你愿意接受“近实时”而非“严格连续”的视角——ASH 给你的是概率分布,不是火焰图
复杂点在于:同一个 sql_id 下,不同执行可能走不同计划,而 ASH 采样点混在一堆 plan_hash_value 里。没过滤好 sql_plan_hash_value 就直接查 DISPLAY_CURSOR,很容易对错号。


















