db file sequential read高通常源于SQL访问路径不合理,而非磁盘性能问题;主因是索引选择不当、回表过多、行迁移/链接或全表扫描被切碎,应优先优化SQL和索引,而非升级存储。
db file sequential read 高,先别动存储
它不是磁盘慢的证据,而是 oracle 正在频繁做单块物理读——绝大多数情况说明 sql 访问路径不合理,比如索引没用对、回表太狠、或全表扫描被“切碎”了。升级 ssd 或调 i/o 调度器大概率白忙,反而掩盖真正问题。
先确认是否真集中在某几条 SQL 上:v$active_session_history 比 AWR 更及时:
SELECT sql_id, current_obj#, object_name, COUNT(*) cnt FROM v$active_session_history a JOIN dba_objects o ON a.current_obj# = o.object_id WHERE event = 'db file sequential read' AND sample_time > SYSDATE - 1/24 GROUP BY sql_id, current_obj#, object_name ORDER BY cnt DESC;
current_obj# 是关键:它指向实际被读的段(可能是索引、表、甚至 undo segment),不是执行计划里写的那个表名。
INDEX RANGE SCAN 后紧跟着 TABLE ACCESS BY INDEX ROWID?小心回表爆炸
这是最常见的高 db file sequential read 场景。索引查得快,但每条结果都要根据 ROWID 去磁盘找对应数据块;如果这些块分散、冷、或大量迁移/链接,一次回表就是一次单块读。
- 检查执行计划:
DBMS_XPLAN.DISPLAY_CURSOR看TABLE ACCESS BY INDEX ROWID的Rows预估 vs 实际是否差 10 倍以上 - 查
v$segment_statistics:对应表的logical reads远高于其索引段,且physical reads持续涨 -
CHAIN_CNT高(来自DBA_TABLES)是行迁移/链接的强信号,直接导致回表变单块读 - 强制
/*+ index_ffs(t idx_name) */可能更快——但前提是只查索引列,否则仍要回表
全表扫描里为啥冒出一堆 db file sequential read?
全表扫描本该走 db file scattered read(多块读),但以下三种情况会让它“碎成单块”:
-
extent太小:比如extent size = 9 blocks,而MBRC = 8,最后一块只能单块读 - 缓存干扰:
MBRC = 8,但其中第 3、7 块已在 buffer cache,Oracle 就拆成三组 I/O,最后一组只剩 1 块 → 触发db file sequential read - 行迁移/链接:每遇到一条迁移行,就得额外一次单块读去新位置取数据
验证是否是迁移:查 DBA_TABLES.chain_cnt;修复方式不是加 buffer cache,而是 ALTER TABLE t MOVE 或导出导入。
为什么加大 db_cache_size 常常没用?
因为单块读多发生在“不可预测的随机访问”场景:比如主键查用户档案,每次请求的块地址几乎不重复。buffer cache 再大,也留不住这些热块——刚缓存完,下一条语句又去读另一个冷块。
更有效的做法是让访问变得可预测:
- 把高频小表塞进 keep pool:
ALTER TABLE t STORAGE (BUFFER_POOL KEEP) - 对索引列加
/*+ index(t idx_col) */强制走索引,但必须确认过滤性足够(WHERE status = 'A'且占比 - 避免在 OLTP 场景中用
INDEX FAST FULL SCAN,它绕过 buffer cache,适合批量统计,不适合点查
最易被忽略的一点:db file sequential read 高,往往不是“读得太慢”,而是“读得太多”。盯住 total_waits 和 time_waited 的比值——如果平均等待时间











