必须限定动态时间窗口,如sample_time > sysdate - interval '3' minute;rac环境须用gv$视图;聚焦db file sequential/scattered read等真实i/o事件;按session_id、sql_id、event统计采样次数定位高延迟会话。

查 v$active_session_history 必须限定时间窗口,否则根本抓不到“单次超20ms”
ASH 是内存中每秒采样一次的滚动缓冲区,默认只保留约 1 小时(高负载下可能更短)。直接 SELECT * FROM v$active_session_history 不加时间过滤,要么查不到刚发生的毛刺,要么扫出大量无效历史数据,响应慢还掩盖问题。抖动类 I/O 延迟往往集中在几十秒内,必须用动态时间表达式锚定。
- 正确写法:
SAMPLE_TIME > SYSDATE - INTERVAL '3' MINUTE(推荐)或SAMPLE_TIME > SYSDATE - 3/1440(兼容老版本) - 别用字符串时间(如
'2026-09-17 14:30:00'),NLS 设置或时区偏差会导致漏样本 - RAC 环境必须用
gv$active_session_history,否则只看到当前节点,而问题可能在 node2
过滤真实磁盘 I/O 等待事件,排除伪 I/O 干扰
不是所有标为 User I/O 的等待都代表磁盘延迟——比如 SQL*Net message from client 或 log file sync 会被误归类。要定位“单次读写超 20ms”的会话,得聚焦真正由存储响应慢触发的事件。
- 只保留:
db file sequential read、db file scattered read、direct path read、direct path write - 必须排除:
event NOT LIKE 'SQL%Net%'、event NOT LIKE 'log file%' -
wait_class = 'User I/O'是必要条件,但不够;必须落到具体 event 名才能判断是否属物理 I/O
用 COUNT(*) 统计采样次数,别信 DELTA_TIME 或平均值
ASH 中每个采样点只记录“当时在等什么”,不记录该次等待已持续多久。DELTA_TIME 字段在短事件中常为 0 或被合并,且 Oracle 19c 后该字段在 ASH 中已弃用。所谓“单次超 20ms”,实际是指:某次采样命中了正在等待的 I/O 事件,而该事件的典型延迟(从 AWR 或历史统计看)已知高于 20ms —— 所以你要找的是高频出现在高延迟时段的会话,而非计算单次耗时。
- 正确做法:按
session_id、sql_id、event分组,统计COUNT(*),再结合 AWR 中该 event 的avg_wait_ms判断是否处于异常区间 - 例如:若 AWR 显示
db file sequential read当前 avg = 25ms,而你在 ASH 里发现某sql_id在最近 2 分钟内占了该 event 总采样的 60%,它就是嫌疑最大者 - 不要写
WHERE time_waited > 20—— ASH 表里根本没有time_waited字段
关联对象与执行计划,确认是不是真在读热点文件
光知道 sql_id 和 event 不够,得确认它读的是哪张表、哪个索引、甚至哪个数据文件。否则优化可能打偏——比如你以为是 SQL 逻辑问题,其实是某个 SYSTEM 表空间里的临时段文件坏了。
- 通过
CURRENT_OBJ# > 0关联dba_objects查对象名;若为 0,说明是 temp、undo 或控制文件操作 - 用
current_file#和current_block#(如有)定位物理位置,再查v$datafile或dba_data_files确认文件路径 - 立刻查该
sql_id的执行计划:DBMS_XPLAN.DISPLAY_CURSOR('xxx', NULL, 'IOSTATS LAST'),看是否出现大量TABLE ACCESS FULL或未走索引的INDEX RANGE SCAN
关键点在于:ASH 不提供“单次等待耗时”,它提供的是“某时刻谁卡在哪个 I/O 事件上”。所谓“抓取单次 I/O 延迟超过 20ms”,本质是借 ASH 锚定正在承受高延迟 I/O 的活跃会话,再靠 AWR 或实时 v$iostat_file 验证底层文件响应是否真超标。漏掉时间过滤、混入伪 I/O、或强行解析 DELTA_TIME,都会让结果完全失真。











