PLSQL程序优化和性能分析方法要点.pdf
《PLSQL程序优化和性能分析方法要点.pdf》由会员分享,可在线阅读,更多相关《PLSQL程序优化和性能分析方法要点.pdf(27页珍藏版)》请在三一文库上搜索。
1、1. 前言 1.1 目的 性能测试是测试中比较重要的工作,性能测试应分为压力的测试和性能的测试,其中性 能问题中绝大部分都是由于程序编写的不合理、不规范造成的。本文档说明了程序中常见的 不优化的脚本编写,导致的性能问题,并且在也描述了怎样去跟踪和解决程序上的性能问题 的方法。 在最后一章里面描述了做一个白盒测试工具测试性能问题的设计思想。 1.2 文档说明 本文档只说明 PLSQL 编写的优化问题,不包括ORACLE 本身的性能优化(内存SGA、 系统参数、表空间等) 、操作系统的性能问题和硬件的性能问题。对于PLSQL 程序优化方面 的内容有很多, 本文档列出在我们实际工作中一些常见的情况。
2、本文档难免有不正确的地方, 也需要大家给予指正。 本文档举例说明的问题语句不是实际程序中真正存在的,只是让大家能看起来更容易理 解,但这些语句也不代表在我们程序中其他部分语句不存在这些问题。 举例说明中的语句采用的是社保核心平台的数据字典,在举例描述中没有标明表名和字 段名的含义,还需单独参考。 1.3 词汇表 词汇名称词汇含义备注 1.4 参考资料 编号资料名称作者日期出版单位 PLSQL 程序优化和性能测试方法 第 2 页 / 共 27 页 1ORACLE SQL 性能优化系列 2 3 2. PLSQL 程序优化原则 2.1 导致性能问题的内在原因 导致系统性能出现问题从系统底层分析也就是
3、如下几个原因: CPU 占用率过高,资源争用导致等待 内存使用率过高,内存不足需要磁盘虚拟内存 IO 占用率过高,磁盘访问需要等待 2.2 PLSQL 优化的核心思想 PLSQL 优化实际上就是避免出现“导致性能问题的内在原因”,实际上编写程序,以及 性能问题跟踪应该本着这个核心思想去考虑和解决问题。 PLSQL 程序占用 CPU 的情况 系统解析 SQL 语句执行,会消耗CPU 的使用 运算(计算)会消耗CPU 的使用 PLSQL 程序占用内存的情况 读写数据都需要访问内存 内存不足时,也会使用磁盘 PLSQL 程序增大 IO 的情况 读写数据都需要访问磁盘IO 读取的数据越多, IO 就越
4、大 大家都知道 CPU 现在都很高,计算速度非常快;访问内存的速度也很快;但磁盘的访问 相对前两个相比速度就差的非常大了,因此PLSQL 性能优化的重点也就是减少IO 的瓶颈, 换句话说就是尽量减少IO 的访问。 性能的优先级 CPU-内存-IO,影响性能的因素依次递增。根据上面的分析,PLSQL 优 化的核心思想为: 1.避免过多复杂的SQL 脚本,减少系统的解析过程 2.避免过多的无用的计算,例如:死循环 3.避免浪费内存空间没有必要的SQL 脚本,导致内存不足 4.内存中计算和访问速度很快 5.尽可能的减少磁盘的访问的数据量,该原则是PLSQL 优化中重要思想。 6.尽可能的减少磁盘的访
5、问的次数,该原则是PLSQL 优化中重要思想。 下面的章节具体介绍常见影响性能的SQL 语句情况。 2.3 ORACLE 优化器 ORACLE 的优化器: PLSQL 程序优化和性能测试方法 第 3 页 / 共 27 页 a. RULE (基于规则 ) b. COST (基于成本 ) c. CHOOSE (选择性 ) 设置缺省的优化器 ,可以通过对init.ora 文件中 OPTIMIZER_MODE参数的各种声明 ,如 RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS . 你当然也在SQL 句级或是会话 (session) 级对其进行覆盖 . 为了使用基于成本的优化器
6、(CBO, Cost-Based Optimizer) , 你必须经常运行 analyze 命令, 以增加数据库中的对象统计信息(object statistics)的准确性 . 如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行 过 analyze命令有关 . 如果 table 已经被 analyze过, 优化器模式将自动成为CBO , 反之, 数据库将采用 RULE 形式的优化器 . 在缺省情况下 ,ORACLE 采用 CHOOSE 优化器 , 为了避免那些不必要的全表扫描(full table scan) , 你必须尽量避免使用CHOOSE 优化器 ,而
7、直接采用基于规则或者基于成本的 优化器 . 在 oracle10g前默认的优化模式是CHOOSE,10g 默认是 ALL_ROWS ,我不建议大家去 改动 ORACLE 的默认优化模式。 2.4 PLSQL 优化 主要说明了在SQL 编写上和PLSQL 程序编写上可以优化的地方。 2.4.1 选择最有效率的表名顺序 只在基于规则的优化器rule 中有效,目前我们oracle 选择的优化器基本都不选择rule,因此该问题基 本不会出现,但为了安全和规范起见,建议编程习惯采用该规则。 ORACLE 的解析器按照从右到左的顺序处理FROM 子句中的表名,因此 FROM 子句中写在最后的表(基 础表d
8、riving table)将被最先处理 . 在 FROM 子句中包含多个表的情况下,你必须选择记录条数最少的表作 为基础表 .当 ORACLE处理多个表时, 会运用排序及合并的方式连接它们.首先 ,扫描第一个表 (FROM 子句 中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个 表中检索出的记录与第一个表中合适记录进行合并. 例如 : 表 ac01有 16,384 条记录 表 ab01 有 1 条记录 选择 ab01作为基础表(好的方法 ) select count(*) from ac01,ab01 执行时间0.96 秒 选择 ac01作为
9、基础表(不好的方法 ) PLSQL 程序优化和性能测试方法 第 4 页 / 共 27 页 select count(*) from ab01,ac01 执行时间26.09 秒 2.4.2 WHERE 子句中的连接顺序 ORACLE 采用自下而上的顺序解析WHERE 子句 ,根据这个原理 ,表之间的连接必须写在其他WHERE 条件之前 例如 : (低效 ) SELECT ab01.aab001,ab02.aab051 FROM ab01,ab02 WHERE ab02.aae140= 31 AND ab01.aab001=ab02.aab001; (高效 ) SELECT ab01.aab001
10、,ab02.aab051 FROM ab01,ab02 WHERE ab01.aab001=ab02.aab001 AND ab02.aae140= 31; 2.4.3 SELECT 子句中避免使用 * 当你想在SELECT 子句中列出所有的COLUMN时,使用动态SQL 列引用* 是一个方便的方法.不幸 的是 ,这是一个非常低效的方法. 实际上 ,ORACLE在解析的过程中, 会将 * 依次转换成所有的列名, 这个 工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。 2.4.4 用 EXISTS 替代 IN 实际情况看,使用exists 替换 in 效果不是很明显,基本一样。 在许多
11、基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用 EXISTS( 或 NOT EXISTS) 通常将提高查询的效率. 低效 : SELECT * FROM ac01 Where aac001 in (select aac001 from ac02 where aab001=str_aab001 and aae140= 31 ); 或 SELECT * FROM ac01 Where aac001 in (select distinct aac001 from ac02 where aab001=str_aab001 and aae140= 31 ); 注意
12、使用distinct 也会影响速度 PLSQL 程序优化和性能测试方法 第 5 页 / 共 27 页 高效 : SELECT * FROM ac01 Where exists (select 1 from ac02 where aac001=ac01.aac001 and aab001=str_aab001 and aae140= 31 ); in 的常量列表是优化的(例如: aab019 in ( 20 , 30 ),不用 exists 替换; in 列表相当于or 2.4.5 用 NOT EXISTS 替代 NOT IN Oracle 在 10g 之前版本not in 都是最低效的语句,虽
13、然在10g 上 not in 做到了一些改进,但仍然还是存在 一些问题,因此我们一定要使用not exists 来替代 not in 的写法。 在子查询中 ,NOT IN 子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN 都是最低效的(因为 它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN , 我们可以把它改写成NOT EXISTS. 例如 : SELECT * FROM ac01 WHERE aab001 NOT IN (SELECT aab001 from ab01 where aab020= 100 ); 为了提高效率.改写为 : SELECT * FRO
14、M ac01 WHERE not exists (SELECT 1 from ab01 where aab001=ac01.aab001 and aab020= 100 ); 2.4.6 用表连接替换 EXISTS 在子查询的表和主表查询是多对一的情况,一般采用表连接的方式比EXISTS 更有效率。 例如: 低效: SELECT ac01.* FROM ac01 Where exists (select 1 from ac02 where aac001=ac01.aac001 and aab001=ac01.aab001 and aae140=31 and aae041=200801); 高效
15、: SELECT ac01.* PLSQL 程序优化和性能测试方法 第 6 页 / 共 27 页 FROM ac02,ac01 Where ac02.aac001=ac01.aac001 and ac02.aab001=ac01.aab001 and ac02.aae140=31 and aae041=200801; 到底 exists 和表关联哪种效率高,其实是根据两个表之间的数据量差别大小是有关的,如果差别不大实际 上速度基本差不多。 2.4.7 用 EXISTS 替换 DISTINCT 当提交一个包含一对多表信息(比如个人基本信息表和个人参保信息表)的查询时 ,避免在 SELECT 子句
16、中 使用 DISTINCT. 一般可以考虑用EXISTS 替换 例如 : 低效 : select distinct ac01.aac001 from ac02,ac01 where ac02.aac001 = ac01.aac001 and ac02.aae140=31 and ac01.aab001=100100; 高效 : select ac01.aac001 from ac01 where exists(select 1 from ac02 where aac001 = ac01.aac001 and aae140=31) and ac01.aab001=100100; EXISTS 使
17、查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果。因此如果 不是特别研究和追求速度的话(例如:数据转换),查询一个表的数据需要关联其他表的这种情况查询, 建议采用EXISTS 的方式。 2.4.8 减少对表的查询 该问题是我们编程中出现过的问题,请大家一定注意,并且该类问题优化可以带来较大性能的提升。 例如 : 低效 cursor cur_kc24_mz is PLSQL 程序优化和性能测试方法 第 7 页 / 共 27 页 Select akc260 from kc24 where akb020 =str_akb020 and aka130= 11 ; curso
18、r cur_kc24_zy is Select akc260 from kc24 where akb020 =str_akb020 and aka130= 21 ; for rec_mz in cur_kc24_mz loop 门诊处理 end loop; for rec_mz in cur_kc24_zy loop 住院处理 end loop; 高效 cursor cur_kc24 is Select akc260,aka130 from kc24 where akb020 =str_akb020 and aka130 in ( 11 , 21 ); for rec_kc24 in cur_
19、kc24 loop if rec_kc24.aka130= 11 then 门诊处理 end if; if rec_kc24.aka130= 21 then 住院处理 end if; end loop; 高效的做法使用同样的条件(或者说是索引)只访问一次磁盘,低效的做法访问了2 次磁盘,这样速度差 别将近 2 倍。 2.4.9 避免循环(游标)里面嵌查询 游标里面不能嵌入查询(或者再嵌游标),其实也不能有update delete 等语句,只能有insert 语句。但在实际 的编程情况下是不可能完全避免的,但我们一定要尽量避免。该类问题也是我们程序中出现过的问题,该 类问题也可以大大提升程序效
20、率,请大家一定注意。 例如: PLSQL 程序优化和性能测试方法 第 8 页 / 共 27 页 低效: Cursor cur_ac04 is Select aac001,akc010 From ac04 Where aab001= prm_aab001; For rec_ac04 in cur_ac04 loop Select aac008 Into str_aac008 from ac01 where aac001=rec_ac04.aac001; if str_aac008= 1 then n_jfje := rec_ac04.akc010*0.08; end if; if str_aac
21、008= 2 then n_jfje := rec_ac04.akc010*0.1; end if; End loop; 高效: Cursor cur_ac04 is Select ac01.aac001,ac04.akc010,ac01.aac008 From ac04,ac01 Where ac04.aac001=ac01.aac001 and aab001= prm_aab001; For rec_ac04 in cur_ac04 loop if rec.aac008= 1 then n_jfje := rec_ac04.akc010*0.08; end if; if rec.aac00
22、8= 2 then n_jfje := rec_ac04.akc010*0.1; end if; end loop; 优化的方法是尽量把游标循环中的查询语句放到游标查询中一起查询出来,这样相当于只访问了1 次磁盘 读到内存;如果放到游标中的话,假如游标有100 万数据量,那么程序需要100 万次磁盘,可以想象浪费 了多少 IO 的访问。 如果在程序编写上没有办法避免游标中有查询语句的话(一般情况是可以避免的),那么也要保证游标中 的查询使用的索引(即查询速度非常快),例如:游标100 万数据量,游标中的查询语句执行需要0.02 秒, 从这个速度上来说是很快的,但总体上看100 万*0.02 秒
23、=2 万秒 =5 小时 33 分钟,如果写一个不够优化的 语句需要1 秒,那么需要几天能执行完呢? PLSQL 程序优化和性能测试方法 第 9 页 / 共 27 页 2.4.10 尽量用 union all 替换 union Union 会去掉重复的记录,会有排序的动作,会浪费时间。因此在没有重复记录的情况下或可以允许有重 复记录的话,要尽量采用union all 来关联。 2.4.11 使用 DECODE 函数来减少处理时间 使用 DECODE 函数可以避免重复扫描相同记录或重复连接相同的表. 例如 : (低效 ) select count(1) from ac01 where aab001
24、= 100001 and aac008= 1 ; select count(1) from ac01 where aab001= 100001 and aac008= 2 ; (低效 ) Select count(1),aac008 From ac01 Where aab001= 100001 and aac008 in ( 1 , 2 ) group by aac008; (高效) select count(decode(aac008, 1 , 1 ,null) zz, count(decode(aac008, 2 , 1 ,null) tx from ac01 where aab001=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- PLSQL 程序 优化 性能 分析 方法 要点
链接地址:https://www.31doc.com/p-5197858.html