direct path read是Oracle绕过Buffer Cache直接读磁盘数据到PGA的信号,真正问题在于其被大量低效触发:1)自动并行查询“偷偷”启用PX进程;2)PGA不足导致频繁temp落盘;3)_serial_direct_read被误设为ALWAYS;需结合v$session_wait、v$px_session、v$tempseg_usage等定位根因。
direct path read 等待事件本身不是问题,而是信号——它说明 oracle 正在绕过 buffer cache,直接从磁盘把数据读进 pga。真正的问题是:为什么它被大量、高频、低效地触发? 常见后果就是物理读陡增、i/o 队列积压、响应变慢,甚至 i/o 100%。
查清是不是并行查询在“偷偷干活”
Oracle 12c+ 默认启用自动并行(_parallel_statement_policy = AUTO),哪怕你没写 PARTITION 或 PARALLEL hint,只要表有并行度设置(ALTER TABLE t PARALLEL 4)或统计信息显示“大表”,就可能拉起 PX 进程——每个从属进程(P00、P01等)都走 direct path read。
- 查
v$session_wait中event = 'direct path read'的会话,看program是否含P00~P99; - 关联
v$px_session,确认这些会话是否属于同一个qcsid(即同一父 SQL); - AWR 报告里看 “SQL ordered by Parallel Executions”,找执行频次高、DOP 大的语句;
- 临时压制:对问题 SQL 加
/*+ NO_PARALLEL */;别碰parallel_max_servers,加它只会让排队更长。
确认是不是 PGA 不够用,被迫频繁落盘
当 ORDER BY、HASH JOIN 或 GROUP BY 的中间结果放不下 PGA,Oracle 就会写入 TEMP 表空间(触发 direct path write temp),再读回来(触发 direct path read temp)。这时你会看到 v$tempseg_usage.segtype = 'SORT' 或 'HASH',且 v$sesstat 中 physical reads direct temp 值飙升。
- 查
v$pgastat:如果extra bytes read/written>bytes processed的 20%,说明 spill 严重; -
PGA_AGGREGATE_TARGET可短期调高(比如 +2G),但必须同步观察cache hit percentage是否改善; - 别动
sort_area_size—— 12c 默认workarea_size_policy = AUTO,该参数已废弃; - 优先改写 SQL:减少
SELECT *、加WHERE条件提前过滤、拆分大UNION ALL。
检查是不是 _serial_direct_read 被设成 ALWAYS
这个隐含参数控制**串行全表扫描**是否走 direct path。默认是 AUTO(Oracle 自判),但若被误设为 ALWAYS,哪怕小表也会强制绕过 Buffer Cache,每次查询都重新物理读——这是最冤枉的 I/O 浪费。
- 查当前值:
SELECT ksppinm, ksppstvl FROM x$ksppi a, x$ksppcv b WHERE a.indx = b.indx AND ksppinm = '_serial_direct_read'; - 如果是
ALWAYS,立刻改回:ALTER SYSTEM SET "_serial_direct_read" = AUTO;(需重启或SCOPE=BOTH); - 临时屏蔽(仅 session 级):
ALTER SESSION SET EVENTS '10949 TRACE NAME CONTEXT FOREVER';; - 注意:不要长期依赖
EVENT 10949,它是诊断手段,不是生产配置。
验证是否真需要走 Direct Path Read
并不是所有 direct path read 都该被消灭。对真正的大表全扫、ETL 加载、报表类查询,它反而是高效路径。关键看两点:是否必要 + 是否可控。
- 查
v$sql_plan:确认operation是TABLE ACCESS STORAGE FULL或LOAD AS SELECT,且options含STORAGE或DIRECT LOAD; - 对比逻辑读/物理读比值:如果
logical reads ≈ physical reads,说明几乎没走缓存——要么表太大,要么缓存被挤出,要么根本没进缓存; - 查
_small_table_threshold(≈db_cache_size * 0.02):若表大小 >5 * _small_table_threshold,Oracle 默认走 direct path;删历史数据、收缩 HWM 或分区可规避; - 存储层响应慢时,
direct path read等待时间(ms)会明显拉长——此时优化重点不在 Oracle 参数,而在 I/O 子系统或 SQL 访问路径(比如加索引)。
真正难处理的,是那些既没走索引、又没控制好并行、还因 PGA 不足反复 spill 的 SQL。它们会让 direct path read 在“本该快”的场景下变成 I/O 放大器。定位时别只盯着等待事件本身,得顺着它往上游追:是谁发起的?为什么不能走缓存?为什么非得排序/哈希?有没有更轻量的访问方式?


















