lob访问默认绕过buffer cache走direct path read,是oracle对行外存储大对象的强制机制;awr中physical reads严重低估其真实io压力,需结合top 5 timed events中的direct path read等待、tablespace io stats延迟及含dbms_lob的sql综合定位。

LOB段产生大量 direct path read 不是配置错误,而是Oracle对大对象访问的底层机制决定的——它默认绕过Buffer Cache,直接从磁盘读到PGA。
LOB访问天然走直接路径
Oracle对LOB字段(BLOB/CLOB)的读取,只要触发了行外存储(即数据超过约4000字节),就会强制使用 direct path read。这不是可选行为,而是设计如此:
- 行内存储(
ENABLE STORAGE IN ROW)的小LOB可能走普通逻辑读,但一旦溢出,立刻切到直接路径 -
DBMS_LOB.READ、DBMS_LOB.SUBSTR等调用底层都映射为LOBREAD操作,trace里会看到WAIT #0: nam='direct path read' - 即使SQL里只
SELECT lob_col FROM t WHERE id = 1,只要该LOB已行外存储,就不会走buffer cache,也不会贡献到buffer hit%统计中
AWR里看不到LOB的“真实”物理读总量
AWR报告中的 Physical Reads 总数严重低估LOB实际IO压力,因为:
-
direct path read的等待事件单独统计在Top Events里,不计入Physical Reads列 - CHUNK大小(默认8KB)导致一次1MB CLOB读取,在AWR中记为约128次
direct path read,IOPS虚高但单次耗时长 - 每次读LOB还要额外访问LOBINDEX块(表现为
db file sequential read),这部分才进Physical Reads,容易被误认为“主表IO不高”
哪些操作会放大direct path read影响
不是所有LOB访问都等价,以下场景会让 direct path read 更频繁、更慢:
- 应用层未做懒加载:比如Java里调用
ResultSet.getBlob().getBinaryStream()却没限制长度,导致整块LOB全读 - LOB字段参与JOIN或ORDER BY:执行计划中出现
LOBSEGMENT或LOBINDEX访问,且无有效谓词过滤 - 表空间未隔离:LOB和普通表共用表空间,
Tablespace IO Stats中Av Rd(ms)升高时无法快速定位是LOB拖慢还是其他对象 - RAC环境下多节点并发读同一LOB:引发
gc cr block busy,等待叠加在direct path read后面,trace里ela值异常高
最常被忽略的一点:你看到的 direct path read 等待时间(Av Rd(ms))偏高,往往不是存储慢,而是LOB chunk分散、索引块争用、或应用一次性拉取远超所需的数据量——优化起点不在IO子系统,而在SQL写法和LOB访问模式本身。











