文章来源:https://mp.weixin.qq.com/s/besccZ-Umrlw-i9vKeqg3Q
想象一下,你去一个超大型图书馆查资料。通常,你会先看看最近热门或者常被借阅的书架,如果找不到,图书管理员会帮你从巨大的书库里把书找出来,放到热门书架,再借给你。但如果一次要查阅的书籍堆积如山,或者这些书非常冷僻,图书管理员可能会觉得,与其把热门书架弄得一团糟,不如直接把这些书用专车送到你的私人阅读室。Oracle 数据库在读取数据时,有时也会采取类似的“抄近道”策略,这就是我们今天要聊的主角——直接路径读 (Direct Path Read)。
这个听起来很高大上的名词,其实是 Oracle 处理数据的一种方式。它有时候能让数据库跑得飞快,有时候却可能成为拖慢系统的“隐形杀手”。那么,它到底是何方神圣?我们又该如何与它“和谐相处”呢?别担心,就算你不是数据库专家,这篇文章也会用最通俗的比喻,带你一探究竟。
什么是直接路径读 (Direct Path Read)?万人共享的图书馆 vs 你的私人书房
要理解直接路径读,我们首先得认识一下 Oracle 数据库内部的两位重要“内存管家”。
Oracle的两大“内存管家”:SGA 与 PGA
在 Oracle 数据库运行时,它会占用服务器的一部分内存来高效处理数据。这部分内存主要分为两大块:
SGA (System Global Area, 系统全局区):可以把它想象成一个公共大图书馆。这个图书馆里有很多资源是所有访问数据库的用户(会话)共享的。其中一个非常重要的区域叫做缓冲区缓存 (Buffer Cache),就像图书馆里存放热门书籍和最近归还书籍的书架。当 Oracle 从磁盘读取数据时,通常会先把数据块放到这个“热门书架”上,方便自己和其他用户下次快速取用。
PGA (Program Global Area, 程序全局区):这个可以看作是每个来图书馆的用户(会话)拥有的私人书房或专属手提袋。这里存放的是该用户自己处理数据时需要的私有信息和工作空间,比如排序时临时存放数据的地方。PGA 中的数据是用户独享的,不与其他用户共享。
常规读取:先到“图书馆热门书架”看看
当你的应用程序需要从 Oracle 读取数据时,通常会发生以下情况(我们称之为常规路径读或缓冲读取):
Oracle 首先会去 SGA 的“热门书架”(Buffer Cache)找找看,有没有你需要的数据块。
如果找到了(称为缓存命中),太棒了!直接从内存中快速读取,然后放到你的“私人书房”(PGA)供你使用。
如果没找到(称为缓存未命中),Oracle 就需要去“大书库”(磁盘)把数据块取出来。
取出来之后,Oracle 会把这个数据块先放到 SGA 的“热门书架”(Buffer Cache)中,方便下次自己或别人使用,然后再复制一份到你的“私人书房”(PGA)。
这种方式的好处是,常被访问的数据会一直待在“热门书架”上,大大减少了访问慢速磁盘的次数,提高了整体效率。
直接路径读:数据直达“私人书房”
现在,轮到我们的主角——直接路径读 (Direct Path Read) 登场了。在某些特定情况下,Oracle 会认为把数据先放到 SGA 的“热门书架”再转到“私人书房”太麻烦,或者可能会把“热门书架”弄乱(比如一次读取非常非常大的数据块,可能会把其他有用的热门数据挤出去)。这时,它会选择一条“捷径”:
直接路径读是指 Oracle 将数据块从磁盘直接读入用户会话的 PGA(进程全局区)中,同时绕过 SGA(系统全局区)的 Buffer Cache。这意味着读入的这部分数据由该会话独自使用,不放在共享的 SGA 中。
简单来说,就像图书管理员直接把你要的一大堆冷门书从大书库用专车运到你的私人书房,不经过公共图书馆的热门书架。这样做,对于处理大量数据或者特定类型的操作可能更高效。
神出鬼没的“直接路径读”:它在哪些情况下出现?
那么,Oracle 会在什么时候决定“抄近道”,启用直接路径读呢?以下是一些常见的场景:
大部头书籍的特殊待遇:全表扫描 (Full Table Scan)
当你需要查找一张非常大的表里的数据,并且没有合适的索引(就像没有目录,只能一页一页翻完整本书),Oracle 可能会执行全表扫描。如果这张表实在太大,Oracle 觉得把它全部加载到 SGA 的 Buffer Cache 里不太划算(可能会把其他更有价值的热点数据挤出去),就可能采用直接路径读。Oracle 11g 版本之后,对于大表的串行全表扫描,更容易触发直接路径读。
Oracle 内部有一个阈值(比如由隐含参数 _small_table_threshold
控制,一般是 Buffer Cache 大小的2%左右),小于这个阈值的表被认为是“小表”,全表扫描时倾向于走 Buffer Cache;大于这个阈值的“大表”则更容易走直接路径读。
书籍太多要整理:排序操作 (Sort Operations)
当你的 SQL 查询中包含排序(如 ORDER BY
)、分组(GROUP BY
)、集合操作(UNION
)、去重(DISTINCT
)等操作时,如果需要处理的数据量非常大,超出了分配给 PGA 中的排序区 (Sort Area) 的大小,Oracle 就需要把中间结果先存放到磁盘上的临时表空间 (Temporary Tablespace) 里,这个过程可能涉及“直接路径写”。之后,当需要从临时表空间把这些排好序的中间结果读回来进行合并时,就可能发生“直接路径读”。
这就好比你要整理一大堆杂乱无章的卡片,但你的书桌(PGA 排序区)太小了,一次摆不下。你只能先在地上(临时表空间)把卡片分批整理好,然后再把整理好的几堆卡片拿回桌上合并成最终有序的一摞。
多人同时搬书:并行查询 (Parallel Query)
为了加快大查询的处理速度,Oracle 可以使用并行查询技术,即派出多个“小弟”(并行从属进程)同时干活。每个“小弟”负责读取和处理一部分数据,它们通常会使用直接路径读,将数据直接读入各自的 PGA 中进行处理,最后汇总结果。这就像多个搬运工同时从仓库的不同区域搬运书籍到各自的小推车上。
其他“快递”场景
除了上述主要场景,还有一些其他情况也可能触发直接路径读/写,例如:
LOB (Large Object) 数据类型的操作:读取或写入如图片、视频等大型对象数据时。
某些数据加载工具:如 SQL*Loader 使用直接路径加载 (Direct Path Load) 模式时,数据会直接写入数据文件,绕过 Buffer Cache。
**哈希连接 (Hash Join)**:当连接的两个表都很大,哈希表无法完全放在内存中时,可能会使用临时表空间,从而引发直接路径读写。
直接路径读:是效率神器还是性能瓶颈?
了解了直接路径读是什么以及何时发生后,一个自然的问题是:它到底是好是坏?答案是——视情况而定。
“快车道”的优势
在某些情况下,直接路径读确实是“快车道”:
处理海量数据更高效:对于数据仓库、批量报表生成等需要扫描和处理大量数据的场景,直接路径读可以避免SGA Buffer Cache的瓶颈,减少内存竞争和管理开销,从而提高吞吐量。
保护Buffer Cache:避免一次性的大数据量读取冲刷掉Buffer Cache中其他有用的热点数据,维持了缓存的稳定性,对OLTP(在线事务处理)系统比较友好。
减少锁存器争用:由于绕过了SGA中的共享数据结构,可以减少对这些结构的锁存器(latch)争用,尤其是在高并发读取大对象时。
“绕远路”的风险:“隐形杀手”的警告
然而,如果直接路径读并非预期行为,或者发生得过于频繁,它也可能变成“绕远路”,甚至成为性能的“隐形杀手”:
物理I/O增加:直接路径读本质上是物理磁盘读取。如果磁盘I/O子系统性能不佳,大量的直接路径读会导致显著的I/O等待,拖慢查询速度。
指示SQL性能问题:频繁的、非预期的直接路径读(尤其是在OLTP系统中对中小表的查询)往往暗示着SQL语句写得不够优化,比如缺少必要的索引导致了全表扫描,或者统计信息不准确导致Oracle选择了错误的执行计划。
PGA内存消耗:数据直接读入PGA,如果并发执行这类操作的会话很多,或者单个会话读取的数据量极大,可能会导致PGA内存消耗过高,甚至引发服务器内存不足的问题。
诊断复杂性:有时直接路径读的等待时间可能不会直接显示为主要瓶颈,但它可能是问题的根源。
遇到“直接路径读”怎么办?给普通用户的建议
如果你在日常工作中听到DBA(数据库管理员)提到“直接路径读”,或者在系统性能报告中看到相关的等待事件,不必过于惊慌。以下是一些应对思路:
首先,它不一定是坏事
再次强调,直接路径读本身是一种优化机制。在数据仓库环境中进行大数据分析、生成复杂报表,或者执行大规模数据加载时,出现直接路径读通常是正常且高效的。如果系统整体运行良好,业务需求得到满足,那么就不必过分纠结于直接路径读的发生。
什么时候需要警惕?
当以下情况发生时,可能就需要关注直接路径读了:
系统整体变慢:用户普遍反映查询响应时间变长,业务处理效率下降。
特定查询性能恶化:某个之前运行正常的查询突然变得非常慢,并且DBA通过AWR报告等工具发现该查询伴随着大量的 "direct path read" 或 "direct path read temp" 等待事件。
I/O压力过大:服务器的磁盘I/O持续处于高位,成为系统瓶颈。
非预期发生:在一些本应快速返回少量数据的OLTP查询中,也出现了直接路径读。
DBA的“诊断工具箱”
如果你怀疑直接路径读是性能问题的元凶,最好的做法是向专业的DBA求助。DBA通常会从以下几个方面进行诊断和优化:
SQL调优
检查执行计划:确认是否因为缺少合适的索引导致了不必要的全表扫描。
创建或重建索引:为查询涉及的列创建合适的索引,可以极大减少全表扫描,从而避免直接路径读。
改写SQL语句:有时候调整SQL的写法,或者使用SQL提示(Hint)可以引导Oracle选择更优的执行路径。
更新统计信息:确保表的统计信息准确,以便Oracle优化器能做出正确的决策。
调整Oracle参数
PGA相关参数:如适当增大
PGA_AGGREGATE_TARGET
(PGA总大小)或SORT_AREA_SIZE
(排序区大小),可以减少排序操作溢出到磁盘的几率,从而减少 "direct path read temp"。**
_small_table_threshold
**:DBA可能会调整这个隐含参数,改变Oracle对“大表”和“小表”的界定标准,从而影响是否采用直接路径读。但修改隐含参数需要非常谨慎。**
DB_FILE_DIRECT_IO_COUNT
**:这个参数控制一次直接路径I/O操作能读取的数据块数量。适当调整可能提升I/O效率,但也需根据硬件能力而定。
表级操作
**
ALTER TABLE ... CACHE
**:如果某个大表确实需要频繁进行全表扫描,并且希望它走Buffer Cache而不是直接路径读,DBA可以考虑将表标记为CACHE状态。但这会增加Buffer Cache的压力。表分区:对于非常大的表,进行合理分区可以使得查询只扫描相关的分区,减少数据读取量。
禁用直接路径读(谨慎使用)
可以通过设置隐含参数
_serial_direct_read = NEVER
(或 FALSE,取决于版本) 来禁用串行全表扫描的直接路径读。也可以通过设置特定的事件如
event '10949'
来影响直接路径读的行为。但通常不推荐全局禁用,因为这可能影响到那些真正受益于直接路径读的操作。这更像是一种临时手段或最后选择。
直接路径读是 Oracle 数据库众多复杂机制中的一个,它体现了数据库在不同场景下追求最优性能的“调度智慧”。它既可以是提升大数据量处理效率的“快车道”,也可能在不当使用或SQL性能不佳时成为拖累系统的“隐形杀手”。
对于非数据库专业人士来说,我们不必深究其所有技术细节,但理解其基本原理、发生场景以及何时可能引发问题,将有助于我们更好地与数据库系统“沟通”,并在遇到性能问题时能更有效地与DBA协作。记住,技术本身无好坏,关键在于是否用对地方,是否符合我们的预期和业务需求。
评论