当前位置:文档之家› PLSQL程序优化和性能分析方法要点

PLSQL程序优化和性能分析方法要点

1.前言1.1目的性能测试是测试中比较重要的工作,性能测试应分为压力的测试和性能的测试,其中性能问题中绝大部分都是由于程序编写的不合理、不规范造成的。

本文档说明了程序中常见的不优化的脚本编写,导致的性能问题,并且在也描述了怎样去跟踪和解决程序上的性能问题的方法。

在最后一章里面描述了做一个白盒测试工具测试性能问题的设计思想。

1.2文档说明本文档只说明PLSQL 编写的优化问题,不包括ORACLE本身的性能优化(内存SGA、系统参数、表空间等)、操作系统的性能问题和硬件的性能问题。

对于PLSQL程序优化方面的内容有很多,本文档列出在我们实际工作中一些常见的情况。

本文档难免有不正确的地方,也需要大家给予指正。

本文档举例说明的问题语句不是实际程序中真正存在的,只是让大家能看起来更容易理解,但这些语句也不代表在我们程序中其他部分语句不存在这些问题。

举例说明中的语句采用的是社保核心平台的数据字典,在举例描述中没有标明表名和字段名的含义,还需单独参考。

1.4参考资料编号资料名称作者日期出版单位2.PLSQL程序优化原则2.1导致性能问题的内在原因导致系统性能出现问题从系统底层分析也就是如下几个原因:●CPU 占用率过高,资源争用导致等待●内存使用率过高,内存不足需要磁盘虚拟内存●IO 占用率过高,磁盘访问需要等待2.2PLSQL优化的核心思想PLSQL优化实际上就是避免出现“导致性能问题的内在原因”,实际上编写程序,以及性能问题跟踪应该本着这个核心思想去考虑和解决问题。

● PLSQL 程序占用CPU的情况⏹系统解析SQL语句执行,会消耗CPU的使用⏹运算(计算)会消耗CPU的使用● PLSQL 程序占用内存的情况⏹读写数据都需要访问内存⏹内存不足时,也会使用磁盘● PLSQL 程序增大IO的情况⏹读写数据都需要访问磁盘IO⏹读取的数据越多,IO就越大大家都知道CPU现在都很高,计算速度非常快;访问内存的速度也很快;但磁盘的访问相对前两个相比速度就差的非常大了,因此PLSQL 性能优化的重点也就是减少IO的瓶颈,换句话说就是尽量减少IO的访问。

性能的优先级CPU->内存->IO,影响性能的因素依次递增。

根据上面的分析,PLSQL优化的核心思想为:1.避免过多复杂的SQL 脚本,减少系统的解析过程2.避免过多的无用的计算,例如:死循环3.避免浪费内存空间没有必要的SQL脚本,导致内存不足4.内存中计算和访问速度很快5.尽可能的减少磁盘的访问的数据量,该原则是PLSQL 优化中重要思想。

6.尽可能的减少磁盘的访问的次数,该原则是PLSQL优化中重要思想。

下面的章节具体介绍常见影响性能的SQL 语句情况。

2.3ORACLE优化器ORACLE的优化器:a.RULE(基于规则)b. COST(基于成本)c. CHOOSE(选择性)设置缺省的优化器,可以通过对init.ora文件中OPTIMIZER_MODE参数的各种声明,如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS. 你当然也在SQL句级或是会话(session)级对其进行覆盖.为了使用基于成本的优化器(CBO, Cost-Based Optimizer) , 你必须经常运行analyze命令,以增加数据库中的对象统计信息(object statistics)的准确性.如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关. 如果table已经被analyze过, 优化器模式将自动成为CBO , 反之, 数据库将采用RULE形式的优化器.在缺省情况下,ORACLE采用CHOOSE优化器,为了避免那些不必要的全表扫描(full table scan) , 你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器.在oracle10g 前默认的优化模式是CHOOSE,10g默认是ALL_ROWS,我不建议大家去改动ORACLE的默认优化模式。

2.4PLSQL优化主要说明了在SQL编写上和PLSQL 程序编写上可以优化的地方。

2.4.1选择最有效率的表名顺序只在基于规则的优化器rule中有效,目前我们oracle 选择的优化器基本都不选择rule,因此该问题基本不会出现,但为了安全和规范起见,建议编程习惯采用该规则。

ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表driving table)将被最先处理. 在FROM 子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE处理多个表时, 会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并.例如:表ac01有16,384 条记录表ab01 有1 条记录选择ab01作为基础表(好的方法)select count(*)from ac01,ab01 执行时间0.96秒选择ac01作为基础表(不好的方法)select count(*)from ab01,ac01 执行时间26.09秒2.4.2WHERE子句中的连接顺序ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE 条件之前例如:(低效)SELECT ab01.aab001,ab02.aab051FROM ab01,ab02WHERE ab02.aae140=’31’AND ab01.aab001=ab02.aab001;(高效)SELECT ab01.aab001,ab02.aab051FROM ab01,ab02WHERE ab01.aab001=ab02.aab001AND ab02.aae140=’31’;2.4.3SELECT 子句中避免使用‘*‘当你想在SELECT 子句中列出所有的COLUMN时,使用动态SQL列引用‘*'是一个方便的方法.不幸的是,这是一个非常低效的方法.实际上,ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。

2.4.4用EXISTS 替代IN实际情况看,使用exists替换in 效果不是很明显,基本一样。

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下,使用EXISTS(或NOT EXISTS)通常将提高查询的效率.低效:SELECT*FROM ac01Where aac001in (select aac001from ac02where aab001=str_aab001and aae140=’31’);或SELECT*FROM ac01Where aac001in (select distinct aac001 from ac02where aab001=str_aab001and aae140=’31’);注意使用distinct也会影响速度高效:SELECT*FROM ac01Where exists (select 1from ac02where aac001=ac01.aac001and aab001=str_aab001and aae140=’31’);in 的常量列表是优化的(例如:aab019in (‘20’,’30’)),不用exists替换;in 列表相当于or2.4.5用NOT EXISTS 替代NOT INOracle在10g之前版本not in 都是最低效的语句,虽然在10g上not in做到了一些改进,但仍然还是存在一些问题,因此我们一定要使用not exists 来替代not in 的写法。

在子查询中,NOT IN 子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN 都是最低效的(因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN,我们可以把它改写成NOT EXISTS.例如:SELECT*FROM ac01WHERE aab001NOT IN (SELECT aab001from ab01where aab020=’100’);为了提高效率.改写为:SELECT*FROM ac01WHERE not exists (SELECT1from ab01where aab001=ac01.aab001 and aab020=’100’);2.4.6用表连接替换EXISTS在子查询的表和主表查询是多对一的情况,一般采用表连接的方式比EXISTS 更有效率。

例如:低效:SELECT ac01.*FROM ac01Where exists (select 1from ac02where aac001=ac01.aac001and aab001=ac01.aab001and aae140='31'and aae041='200801');高效:SELECT ac01.*FROM ac02,ac01Where ac02.aac001=ac01.aac001and ac02.aab001=ac01.aab001and ac02.aae140='31'and aae041='200801';到底exists 和表关联哪种效率高,其实是根据两个表之间的数据量差别大小是有关的,如果差别不大实际上速度基本差不多。

2.4.7用EXISTS 替换DISTINCT当提交一个包含一对多表信息(比如个人基本信息表和个人参保信息表)的查询时,避免在SELECT 子句中使用DISTINCT.一般可以考虑用EXISTS替换例如:低效:select distinct ac01.aac001from ac02,ac01where ac02.aac001=ac01.aac001and ac02.aae140='31'and ac01.aab001='100100';高效:select ac01.aac001from ac01where exists(select1from ac02 where aac001= ac01.aac001and aae140='31')and ac01.aab001='100100';EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果。

因此如果不是特别研究和追求速度的话(例如:数据转换),查询一个表的数据需要关联其他表的这种情况查询,建议采用EXISTS 的方式。

相关主题