Oracle数据库中大型表查询优化研究

2/9/2008来源:Oracle教程人气:5094

摘 要:对海量数据进行访问查询时,经常碰到系统响应时间过长,占用系统资源过多的问题。本文结合实例着重对Oracle数据库中的查询优化进行了研究,测试结果表明采用的方法是很有效的,大大缩短了测试用例表的响应时间,最后对海量数据的优化方法提出了实用性的建议。 要害词:海量数据;Oracle数据库;查询优化;数据查询。 1 引 言 在直升机飞行地面数据处理平台中,需要查询历史飞行数据来进行飞行状态的模拟及其飞行事故的分析,从而对当前飞机状态进行评判。其数据量非常巨大。如何对其进行快速访问,提高系统响应时间就显得十分重要。在实际应用中,往往采用各种优化措施,使得SQL查询经过数据库优化器的处理,得到最佳的执行计划,即数据访问路径,来达到提高响应速度的目的。由于项目采用的是Oracle数据库,以下考虑对Oracle数据库进行的优化情况。 2 Oracle查询顺序及其调整 Oracle优化的一般顺序如下:环境调整(服务器、网络、磁盘)、Oracle实例调整、Oracle对象调整、Oracle SQL调整。我们的测试采用的Oracle数据表容量为50 M,记录条数为50万条。测试方法为:根据用户的查询要求计算得到用户需要浏览的数据记录的起止位置,然后在SQL语句中加入此位置,执行SQL语句,查询该数据表,得到用户想要浏览的记录集合。使 用的SQL语句如下:
  SELECT*FROM(SELECT*FROM(SELECT*FROM BIG) WHERE ROWNUM<TOPOS ORDER BYROWNUM DESC)WHERE ROWNUM<TOPOSFROMPOS+1;
使用该SQL查询得到结果集需要5~6 s,这个响应速度难以满足用户浏览要求,因此必须对其优化以提高响应速度。我们是在假定环境调整已经完成的条件下通过对Oracle实例、对象、SQL查询语句的调整得出结论的,其中重点是对SQL语句的调整。 2.1 实例调整 首先进行Oracle实例调整。Oracle实例涉及到SGA内存区和一组Oracle后台处理进程。对Oracle实例的调整就是对SGA内存区和Oracle后台处理进程的调整。在对该问题的解决中,主要是针对SGA内存区的调整。 2.1.1 SGA内存区结构 SGA就是系统全局区,是指内存中答应多个进程相互通信的区域。在Oracle中,SGA对所有进程来说都是全局的可用的。图1为SGA结构图。 缓冲区高速缓存是SGA中为所有用户和系统进程保存数据的区域,任何数据在传递给一个调用的应域是共享的,所以多个进程可以从这片高速缓存读取同样的数据块,而不必每次都从物理磁盘中读取。
   共享池是SGA中的另一个区域,其中保存着关于待执行的SQL语句的信息。他由两部分组成:数据字典高速缓存,存放从数据字典中读取的信息以用于处理SQL请求;库高速缓存,存放需要执行的SQL语句信息,包括每个SQL语句的语法分析树和执行计划。假如多个用户要执行同样的SQL语句,那么语法分析树和执行计划就可以重复利用,省去了语法分析步骤的昂贵花费。 2.1.2 调整SGA结构 一般来讲,在系统硬件支持的情况下,系统全局区越大越有利于数据库高效的运行。大的缓冲区高速缓存可以缓存更多的数据块,这样可以提高缓存命中率,节省物理磁盘读取的高昂代价;大的共享池意味着大的库高速缓存。库缓存的内存结构如图2所示。
   库缓存越大,可以保存的SQL语法分析信息越多;此外,数据库中的一些对象,如表、索引、过程、触发器、软件包等也在首次执行后进驻库高速缓存。大的库缓存可以保证对这些对象的高命中率,从而节省解析和载入代价。 作为一个通用的优化原则,我们在解决该问题时,适当增大了SGA的容量,从而保证Oracle实例可以比较高效的运行。设置缓冲区高速缓存的容量为32 M,设置共享池的大小为56 M。运行rpt_lib.sql程序检查库高速缓存不足率(还没有运行该脚本),表明库缓存足够。 2.2 对象调整 这一步骤我们要对每一个Oracle对象进行调整从 而优化性能,包括对所有的存储参数进行正确的设置,尤其是对影响输入输出的参数进行设置。Pctfree,pctused,freelist参数的设置都会对SQL性能产生重要影响。 在解决该问题时,我们没有对对象的存储参数进行调整。对象的存储参数的调整要建立在具体对象的基础之上,我们使用的测试大表除了在容量上模拟可能的实际对象之外,和实际对象并没有多少可比之处。举例来说,pctfree参数是指在实际的存储块中,留出来为块中的记录扩展所用的空闲空间占该存储块容量的百分比,这个参数和实际表记录的长度及可扩展性有关,要在设计出实际表之后进行调整。因此我们对对象的存储参数使用了数据库的缺省设置。数据库的对象还包括索引、过程、包等,对对象的调整要包括对他们的调整,这里一并略去。 2.3 SQL语句调整 SQL语言是一种灵活的语言,相同的功能可以使用不同的语句来实现,但是语句的执行效率是很不相同,一般考虑如下规则: (1)添加索引 在对大表进行扫描时,首先要避免不必要的全表扫描。最通常的做法就是给大表添加索引。所谓全表扫描,就是在访问表时,从磁盘上存储该表的起始位置开始逐记录读数据,直到该表的结束位置。给大表添加索引后,我们可以通过访问索引的方式获得记录的物理位置,从而达到访问表的目的。设想一下,对于一个拥有大量字段的表,假如只需要返回其中少量字段,那么在这些字段上建立索引,通过索引访问获得记录,将大大降低物理磁盘读写次数,从而降低了整个查询响应时间(事实上,这种情况下根本没有必要访问数据表,只是访问索引就足够了)。在我们的测试用表中,只有4个字段,显然无法发挥索引的这一优势。我们的测试用表的特点是记录条数多,达到50万条,但每次返回的记录数只有几十条,在这种情况下,使用索引同样可以起到好的效果。首先在检索条件上建立索引,在表访问时,我们通过索引来获取目的记录集的物理地址,因为返回的记录数不多,所以这种方式造成的物理读写很少,应该可以获得较满足的访问时间。在测试中,我们对测试用表的条件字段添加索引,但事实上并没有提高查询的响应时间,这是因为在SQL语句的执行计划中,在添加索引之前和之后,Oracle生成的执行计划中,表访问方式都是全表扫描,根本没有使用我们添加的索引。那么此时我们可以采用添加提示的方法。 (2)添加提示 在Oracle中,我们可以在SQL语句中加入提示,来影响Oracle对优化模式的选择,从而生成最优的执行计划。于是我们在SQL语句中加入提示,强制优化器在生成执行计划时将表的访问方式从全表扫描改为索引范围扫描。结果该查询花费了比全表扫描多得多的响应时间。仔细查看这个SQL语句的执行计划,我们发现Oracle对这个索引进行了全索引扫描,这样造成的物理读写数量比起全表扫描不仅没有减少,反而大幅上升。寻找原因,问题出在我们索引的字段上。我们索引的AGE字段为了测试方便,每条记录的值都不相同,这样的字段其实并不具备建立索引的条件。在该测试中,我们首先要将索引载入内存,由于索引各不相同,Oracle采取了全索引扫描的方式访问索引,我们要读取的是30万条记录(AGE>200 k),通过索引,我们获得的将是30万个记录的ROWID,假如这些记录在磁盘上的物理位置没有按索引顺序排序,那么我们就不得不通过30万个ROWID来访问这些记录,这样造成的物理读写是相当惊人的,这就是为什么我们使用了索引,却反而查询速度更慢的原因所在。事实上Oracle的优化器选择全表访问是已经对不同的执行计划作过比较的了。同时,这里同样违反了一个Oracle优化的准则,对于没有按索引顺序排序的表,假如检索记录数小于总记录数的7%,用索引比全表扫描快。 (3)更改优化器模式 优化器是Oracle数据库中接受来自产生器处理过的SQL语句的程序。他将对SQL进行优化,生成内部执行计划,这个计划是Oracle存取物理磁盘数据的路径。根据优化器模式的不同,生成的内部执行计划也不同,而对于相同的SQL,永远只有一个最优的执行计划,因此选择合适的优化器模式,是很重要的。Oracle有2种优化器模式:基于规则的优化模式和基于成本的优化模式。在这次测试中,我们使用的优化器模式参数为CHOOSE,这样Oracle将根据SQL语句相关表索引的有无,统计资料的有无以及SQL语句中的提示,自动判定使用哪种优化模式。 (4)将调整持久化 Oracle的执行计划是根据各种情况,比如表的统计资料变化的,但有时这种变化是我们不希望的。为了将我们已经调整好的SQL执行计划固定,我们可以 用Oracle的工具将执行计划持久化存储。 2.4 调整结果 通过对这个测试用例的调整,我们最终将此大表的响应时间从5 s缩短到不足2 s。经过调整后的缓冲区高速缓存达到113 M,大约占物理内存的50%,调整后的SQL语句为:
  SELECT name,age,memo FROM (SELECTname,age,memo,rownum ASmynum FROM test2) WHEREmynum>400000 and mynum<=400010;
我们测试所用的机器配置为CELETRON500 M,内存256 M,硬盘为IDE8.4 G。可以说是运行Oracle8I的最低配置。启动Oracle 8I数据库后,内存使用达到300 M以上,也就是说已经在用虚拟内存;测试开始后,CPU保持或接近满载,这些都会影响Oracle的性能表现,也会影响测试结果。 3 结 语 以下几点可以作为通用的Oracle SQL调整原则: (1)消除不必要的全表扫描,可以通过添加索引达到。 (2)缓存小型表的全表扫描,可以通过将小型表置入缓冲区高速缓存的KEEP池中实现。 (3)假如表有多个索引,要保证Oracle正在使用对此SQL最优化的索引,可以通过添加提示实现。