Oracle在读取数据时存在两种截然不同的路径:一种是经典的多块读,将数据块读入Buffer Cache供所有会话共享;另一种是Direct Path Read直接路径读取,数据块直接读入会话的PGA私有区域,完全不经过SGA中的缓冲池。很多数据库工程师在AWR报告中看到大量direct path read等待事件时往往一头雾水,不清楚它为何出现、是否需要处理。这篇文章将从原理层面解释直接路径读取的机制,梳理常见的触发场景,并给出排查和优化的实践方法。

一、Direct Path Read的基本原理与触发条件
在传统的缓存读模式下,Oracle通过Buffer Cache管理数据块,借助LRU算法让热点数据常驻内存,多个会话可以共享同一份数据。而直接路径读取会话在PGA中分配私有缓冲区,服务进程直接发起对数据文件的读取,块内容不进入Buffer Cache。这样做的最大好处是避免了缓存污染——一次性的大批量扫描不会把热点业务数据从内存中挤出去,同时也绕过了cache buffers chains latch,减少了并发争用。
那么哪些操作会走直接路径读取呢?最典型的是大表的全表扫描。从11g开始,Oracle引入了自适应直接路径读取机制,优化器会根据对象的大小与Buffer Cache的比例来决定是否绕过缓存。当表的大小明显超过缓冲区缓存的一定比例时,即使没有显式的并行或hint,Oracle也可能自动选择直接路径方式访问。此外,以下场景几乎必然出现direct path read等待事件:
- 并行查询(PARALLEL)执行的表扫描,从11g起并行扫描强制走直接路径;
- 排序、哈希连接等操作落盘后,临时段数据的回读;
- 使用LOB时通过LOB接口读取大对象内容;
- RMAN备份、数据泵导出等读取数据文件的工具类操作;
- 显式指定了hint的查询。
理解这些触发条件非常重要,因为同样是direct path read,正常的大批量扫描和失控的串行全表扫描对系统的影响完全不同,处理方式也不同。
二、如何识别与诊断直接路径读取
识别直接路径读取主要依靠动态性能视图和AWR报告。最直接的信号是在v$session中观察到等待事件为direct path read的会话,或者在AWR的Top Foreground Events中看到该事件占用了大量DB Time。可以结合以下SQL快速定位正在进行直接路径读的会话及其访问对象:
SELECT s.sid,
s.username,
s.event,
s.p1,
s.p2,
s.p3,
sql.sql_text
FROM v$session s
LEFT JOIN v$sql sql
ON s.sql_id = sql.sql_id
WHERE s.event = 'direct path read';如果需要更精细的分析,可以开启10046跟踪或使用v$direct_optimizer_stats等手段观察IO分布。此外,Oracle提供了几个关键参数影响直接路径读取的行为:_serial_direct_read控制串行全表扫描是否允许走直接路径,_small_table_threshold决定了多大算小表的判断阈值,_very_large_object_threshold则影响大对象的自适应策略。这些隐藏参数一般不建议随意修改,但了解它们有助于理解优化器的决策逻辑。
诊断时还要区分normal和direct path两种读取的判断。可以通过v$sesstat中的table scan blocks gotten与physical reads direct统计值对比,或者查看执行计划的IN-MEMORY列提示。如果一条SQL的扫描方式在缓存读和直接路径读之间频繁切换导致执行计划不稳定,往往就是小表阈值边缘的对象造成的。
三、直接路径读取的性能影响与调优实践
直接路径读取本质上并非坏事,它在大批量数据访问场景下是高效的。但一旦在不该出现的场景出现,就可能成为性能杀手。典型的负面影响包括三个方面:一是单块读放大,直接路径读取会跳过Buffer Cache中已经缓存的热块,反复从磁盘读取本可共享的数据;二是每次执行都会发生物理IO,对于频繁执行的SQL,等于放弃了缓存复用;三是在11g的自适应机制下,执行计划可能在两种读取方式之间抖动,导致性能忽好忽坏。
针对这些问题,常见的调优思路有以下几种。首先是SQL层面优化,检查是否缺少索引导致大表被全表扫描,为高频过滤条件建立合适的索引可以从根源上消除不必要的直接路径读。其次,如果确认表数据几乎全部被访问且走缓存更划算,可以在会话或语句级别禁用串行直接路径:
-- 语句级别禁用直接路径读取
SELECT /*+ OPT_PARAM('_serial_direct_read','never') */ *
FROM big_table
WHERE create_date >= TRUNC(SYSDATE);
-- 会话级别修改(需谨慎)
ALTER SESSION SET "_serial_direct_read" = never;再次,对于确实需要大批量扫描的报表类SQL,可以考虑将表放入内存列存储(In-Memory Column Store),或者使用并行执行让直接路径读取的优势充分发挥,因为并行扫描本身与直接路径就是天作之合。最后,从存储层面看,如果direct path read等待的IO延迟本身就很高,说明瓶颈在底层存储,应检查存储设备性能、ASM磁盘组均衡度以及操作系统的异步IO配置,确保db_file_multiblock_read_count和文件系统的预读设置合理。
总的来说,面对direct path read等待事件,正确的做法是先弄清楚它是谁触发的、访问的是什么对象、属于哪类业务场景,再决定是优化SQL、调整缓存策略还是优化存储,而不是一味地试图消灭它。理解Oracle选择直接路径读取背后的逻辑,才能在大表访问、并行查询和报表系统中做出准确的性能判断。
OracleDirect Path Read直接路径读取修改时间:2026-08-31 21:00:57