user i/o wait占比高且avg wait上升,本质是缓存失效或访问模式突变,而非磁盘故障;主因包括direct path read绕过buffer cache、sql计划频繁失效、大表统计信息锁争用及配置不当(如_small_table_threshold过小)。

User I/O Wait Class 占比高 + Avg Wait (ms) 同步上升,不是“IO慢了”的结论,而是信号:数据库正在反复读取同一类块,且无法高效复用或预取——本质是缓存失效或访问模式突变。
为什么User I/O Wait占比高不等于磁盘坏了
AWR里User I/O Wait Class占比超过40%,同时db file sequential read平均等待从5ms跳到22ms,很多人第一反应是换SSD。但真实场景中,80%以上这类问题跟存储硬件无关:
- Buffer Cache被大量
direct path read绕过(比如大表全扫、并行查询),导致物理读暴增,而这些读又没触发有效预读,每块都得单独发I/O - Shared Pool里SQL计划频繁失效,硬解析后生成新执行计划,引发大量
db file scattered read(索引快速全扫)和db file sequential read(回表) - 某张大表被
ANALYZE TABLE ... COMPUTE STATISTICS锁住,其他会话在等latch: cache buffers chains,间接拖慢所有涉及该表的I/O路径 -
DB_FILE_MULTIBLOCK_READ_COUNT设得过大(如128),但实际存储单次IO吞吐跟不上,反而把一次逻辑读拆成多次低效物理读
必须立刻查的三个视图组合
别只盯AWR报告首页的Top Events。以下三组查询要一起跑,才能锁定根因:
- 看是否是
direct path read主导:SELECT event, total_waits, time_waited_micro/1000000 AS sec FROM v$system_event WHERE event IN ('db file sequential read','db file scattered read','direct path read') ORDER BY time_waited_micro DESC; - 查哪些SQL在刷Cache:
SELECT sql_id, executions, buffer_gets, disk_reads FROM v$sql WHERE disk_reads/executions > 100 AND executions > 10 ORDER BY disk_reads DESC FETCH FIRST 5 ROWS ONLY; - 确认Buffer Cache压力源:
SELECT name, value FROM v$sysstat WHERE name IN ('physical reads','physical reads direct','session logical reads');若physical reads direct/physical reads> 0.6,说明大部分I/O来自绕过Buffer Cache的操作
常见但容易被忽略的配置陷阱
很多DBA调完DB_CACHE_SIZE就以为万事大吉,其实以下配置才是隐性放大器:
-
_small_table_threshold默认是2% ×DB_CACHE_SIZE,如果设得太小(比如200),哪怕一张20MB的表也会被判定为“大表”,强制走direct path read -
optimizer_index_caching长期设为0,导致CBO低估索引扫描成本,倾向选择全表扫描+大量db file sequential read - RAC环境下
gc_files_to_locks未按数据热点调整,跨实例块请求引发重复物理读(尤其在buffer busy waits同步上升时) - 启用了
inmemory但未对高频小表执行ALTER TABLE ... INMEMORY,结果热数据既不在IM列式区,也不在Buffer Cache里,只能反复物理读
真正难处理的,是那些db file sequential read平均延迟稳定在18–25ms、但v$iostat_file里对应文件的avg_read_ms却只有1.2ms的情况——这说明问题不在磁盘响应,而在Oracle自己读块的顺序和频率上。这时候再盯着iostat看,只会浪费两小时。











