_db file sequential read 是oracle等待单块物理读完成的事件,反映sql频繁访问不在内存中的热块,主因是访问模式与内存分配不匹配,而非i/o故障。
什么是 _db file sequential read 等待事件
它不是“慢”,而是 oracle 在等单块物理读完成——即从磁盘读一个数据块到 buffer cache 的过程。只要看到这个等待占高,基本说明 sql 正在频繁访问不在内存里的热块(比如索引叶节点、小表的随机行),而不是全表扫或大批量读。
关键点:这不是 I/O 子系统故障的直接信号,而是访问模式和内存分配不匹配的结果。
- 常见于走索引回表、ORDER BY + LIMIT、绑定变量导致执行计划漂移后索引选择率骤降的场景
- 和
db file scattered read(多块读)不同,它每次只读 1 个 block,event参数 p1/p2/p3 分别对应文件号、块号、读取块数(恒为 1) - 如果平均等待时间
time_waited_micro / waits超过 10ms,才值得怀疑存储响应能力;低于 5ms 基本是正常机械盘延迟,优化重点应转向减少读次数
怎么定位真正触发它的 SQL 和对象
不能只看 AWR 报告里“Top Events”,得顺藤摸瓜找到具体是哪个对象、哪条语句在反复单块读。
推荐用 v$active_session_history(ASH)反查最近 1 小时的采样:
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#是关键,它指向正在被读的段(可能是索引、表、LOB segment),不是执行计划里写的那个表名 - 注意
object_name可能为空(比如读的是 undo 或 temp segment),此时要结合file#和block#查dba_extents - 别依赖
v$sql_plan的object_owner/object_name,它反映的是解析时的对象,而实际运行时可能因分区裁剪、物化视图重写等指向别的段
为什么加大 db_cache_size 常常没用
因为单块读多发生在“访问稀疏、不可预测”的场景:比如通过主键查用户档案,每次请求的 block 地址几乎不重复。缓存再大,也架不住 buffer cache 里刚腾出位置,下一条 SQL 又去读另一个冷块。
- 典型无效操作:把
db_cache_size从 4G 拉到 16G,但buffer cache hit ratio只从 89% → 91%,_db file sequential read等待时间纹丝不动 - 真正有效的是让这些块“变得可预测”——比如把高频随机访问的小表强制 keep 到
keep pool,或对索引列加/*+ index() */提示避免执行计划走错路径 - 还要检查
optimizer_index_caching和optimizer_index_cost_adj是否被设成极端值(如 0 或 100),这会让 CBO 低估索引回表成本,反而选错执行路径
哪些配置和操作会悄悄加重单块读压力
有些 DBA 认为“我关了归档、开了异步 I/O、用了 SSD”,问题就该消失——但实际中,几个隐蔽设置反而在放大单块读频率。
-
cursor_sharing = force导致硬解析生成泛化执行计划,索引范围扫描变成全索引扫描,再配合ROWNUM ,Oracle 不得不逐块读、逐块判断,直到凑够 N 行 - 表上建了
function-based index但查询没用对应函数表达式,CBO 放弃走索引,退化为索引全扫 + 单块回表 -
db_file_multiblock_read_count设得过大(比如 128),虽利于全表扫描,但会让 CBO 更倾向认为“全扫比索引回表便宜”,间接增加后续单块读总量 - 使用
DBMS_STATS.GATHER_TABLE_STATS时没传method_opt => 'FOR ALL INDEXED COLUMNS SIZE AUTO',导致索引列直方图缺失,CBO 误判选择率,选错驱动表或连接方式
最常被忽略的是:单块读本身不可怕,可怕的是它出现在不该出现的地方——比如一个本该走全表扫描的报表 SQL,因为某个字段加了索引又没收集统计信息,结果每秒触发上千次 _db file sequential read。这时候修索引不如删索引,调参数不如改 SQL。











