客户新上线的一套重要生产系统,某个存储过程每小时调用约11万次,每次调用的逻辑读超过了10000,消耗的CPU占数据库CPU Time的25-30%。很显然,这样一个存储过程是值得优化的。

不幸的是,这个存储过程的业务逻辑很复杂,光是参数就有9个。而存储过程所在的包其代码超过了1万行。通过复查代码的方式,耗时耗力,还不一定能够找出问题。

诊断性能问题,首要的是了解这个存储过程所大概要完成的业务功能,然后通过trace或instrument收集足够详细的性能数据。从客户和开发商那里了解到,存在性能问题的存储过程,主要用于系统之间的数据查询接口,根据不同的参数输入查询不同的数据,那么,对于这种小数据量的存储过程,可以考虑使用10046事件来分析是哪些SQL产生了这么多的逻辑读。

但是通过10046事件,发现存储过程中实际执行的SQL并不多,同时并没有逻辑读高的SQL语句。也许问题并没有出现在SQL语句中,而是出现在存储过程中其他非SQL部分。虽然用10046没有找到SQL语句,但还是有重大发现,在过程执行时,大量的逻辑读来自于current方式的读,这显示不是通常的SELECT语句所产生的。接下来我们用dbms_profiler来分析存储过程:

view plain copy to clipboard print ?
  1. select dbms_profiler.start_profiler from dual;
  2. exec intf.CRM_SERVICE_INTF.QueryService(.....);
  3. exec dbms_profiler.stop_profiler;

然后使用来自MOS文档“Implementing and Using the PL/SQL Profiler [ID 243755.1]”中的profiler.sql脚本,生成一个profiler的结果文件,格式为html。下面是部分的内容:

点击其显示的代码行(line),跳转到相应的源代码,发现大量的字符串拼接代码,很明显是用于拼接成XML格式。这很容易理解,因为现在系统之间的文本数据交互,xml几乎成了标准。从上面的截图中也可以看到很多类型于xml:=xml || ‘xxxx’ 这样的代码。是这样的代码引起的问题吗?

检查代码发现,这里用于拼接字符串的变量xml,被定义为clob类型,这引起了我极大的关注。在oracle的标准数据类型中,lob类型由于其能够存储大数据的本质,导致其内部格式和操作是最复杂的。有理由怀疑是clob的大量拼接引起的问题。

在有怀疑对象后,我们可以构造下面的测试来进行验证:

首先创建下面3个不同的存储过程,但是实现的功能是一致的:

view plain copy to clipboard print ?
  1. create or replace procedure p1 ( v_out out clob)
  2. is
  3. v_lob clob;
  4. begin
  5. v_lob:='';
  6. for rec in (select object_name from dba_objects where rownum< =1000) loop
  7. v_lob:=v_lob || rec.object_name;
  8. end loop;
  9. v_out:=v_lob;
  10. end;
  11. /
  12. create or replace procedure p2 ( v_out out clob)
  13. is
  14. v_lob varchar2(32767);
  15. begin
  16. v_lob:='';
  17. for rec in (select object_name from dba_objects where rownum<=1000) loop
  18. v_lob:=v_lob || rec.object_name;
  19. end loop;
  20. v_out:=v_lob;
  21. end;
  22. /
  23. create or replace procedure p3 ( v_out out clob)
  24. is
  25. v_lob clob;
  26. v_str varchar2(32767);
  27. v_cnt number;
  28. begin
  29. v_lob:='';
  30. v_cnt:=0;
  31. v_str:='';
  32. for rec in (select object_name from dba_objects where rownum<=1000) loop
  33. v_str:=v_str || rec.object_name;
  34. v_cnt:=v_cnt+1;
  35. if v_cnt = 50 then
  36. v_cnt:=0;
  37. v_lob:=v_lob || v_str;
  38. v_str:='';
  39. end if;
  40. end loop;
  41. if v_cnt <>0 then
  42. v_lob:=v_lob || v_str;
  43. end if;
  44. v_out:=v_lob;
  45. end;
  46. /

第1个存储过程,P1,完全使用clob类型来拼接字符串;第2个存储过程,P2,使用varchar2类型拼接字符串,只有在过程最后将varchar2转成clob;第3个存储过程,P3,先使用varchar2类型拼接成较长的字符串,然后再用较长的字符串用clob类型来拼接。

下面看看P1和P2的差距:

view plain copy to clipboard print ?
  1. SQL> var v_lob1 clob;
  2. SQL> var v_lob2 clob;
  3. SQL> begin
  4. 2    sys.runstats_pkg.rs_start;
  5. 3    p1(:v_lob1);
  6. 4    sys.runstats_pkg.rs_middle;
  7. 5    p2(:v_lob2);
  8. 6    sys.runstats_pkg.rs_stop;
  9. 7  end;
  10. 8  /
  11. Run1 ran in 5 hsecs
  12. Run2 ran in 0 hsecs
  13. run 1 ran in 50000000% of the time
  14. Name                                  Run1        Run2        Diff
  15. STAT...lob reads                       999           0        -999
  16. LATCH.cache buffers lru chain        1,000           0      -1,000
  17. STAT...free buffer requested         1,012           9      -1,003
  18. STAT...lob writes                    2,000           1      -1,999
  19. STAT...lob writes unaligned          2,000           1      -1,999
  20. LATCH.object queue header oper       2,024           9      -2,015
  21. STAT...consistent gets               3,581         580      -3,001
  22. STAT...consistent gets from ca       3,581         580      -3,001
  23. STAT...consistent changes            5,051          42      -5,009
  24. STAT...db block changes              5,061          52      -5,009
  25. STAT...calls to get snapshot s       7,008          10      -6,998
  26. STAT...db block gets from cach      14,075          57     -14,018
  27. STAT...db block gets                14,075          57     -14,018
  28. STAT...session logical reads        17,656         637     -17,019
  29. LATCH.cache buffers chains          46,151       1,083     -45,068
  30. STAT...session uga memory          138,608      15,072    -123,536
  31. STAT...session uga memory max      315,052     131,024    -184,028
  32. STAT...session pga memory max      393,216     131,072    -262,144
  33. STAT...session pga memory          393,216      65,536    -327,680
  34. Run1 latches total versus runs -- difference and pct
  35. Run1        Run2        Diff       Pct
  36. 49,789       1,674     -48,115  2,974.25%
  37. PL/SQL 过程已成功完成。

从上面的数据来看,差距是巨大的,执行P1时产生的一致性读(consistent gets)和当前模式读(db block gets),都远远大于执行P2时产生的一致读和当前模式读。特别是大量的db block gets验证了之前使用10046事件跟踪存储过程执行得到的结果。上面的数据中,甚至是会话消耗的内存都有很明显的差异。同时,还有其他对性能显著影响的地方,cache buffers chains latch,cache buffers lru chain latch,object queue header operation latch,这3种latch的获取次数是相当多的,在大并发时无疑将引起争用。实际上,在客户的这套系统中,这3种latch的争用经常出现,只是目前还没形成特别严重的后果。

再看看P1和P3的差异:

view plain copy to clipboard print ?
  1. SQL> var v_lob1 clob;
  2. SQL> var v_lob2 clob;
  3. SQL> begin
  4. 2    sys.runstats_pkg.rs_start;
  5. 3    p1(:v_lob1);
  6. 4    sys.runstats_pkg.rs_middle;
  7. 5    p3(:v_lob2);
  8. 6    sys.runstats_pkg.rs_stop;
  9. 7  end;
  10. 8  /
  11. Run1 ran in 7 hsecs
  12. Run2 ran in 2 hsecs
  13. run 1 ran in 350% of the time
  14. Name                                                      Run1        Run2        Diff
  15. STAT...lob reads                       999          19        -980
  16. LATCH.cache buffers lru chain        1,002          20        -982
  17. STAT...free buffer requested         1,012          29        -983
  18. STAT...lob writes                    2,000          40      -1,960
  19. STAT...lob writes unaligned          2,000          40      -1,960
  20. LATCH.object queue header oper       2,016          49      -1,967
  21. STAT...consistent gets from ca       3,579         639      -2,940
  22. STAT...consistent gets               3,579         639      -2,940
  23. STAT...consistent changes            5,051         143      -4,908
  24. STAT...db block changes              5,061         153      -4,908
  25. STAT...calls to get snapshot s       7,007         146      -6,861
  26. STAT...db block gets                14,075         346     -13,729
  27. STAT...db block gets from cach      14,075         346     -13,729
  28. STAT...session logical reads        17,654         985     -16,669
  29. LATCH.cache buffers chains          46,147       1,999     -44,148
  30. STAT...session uga memory          189,012      65,464    -123,548
  31. STAT...session pga memory          196,608      65,536    -131,072
  32. STAT...session pga memory max      262,144      65,536    -196,608
  33. STAT...session uga memory max      315,052      65,560    -249,492
  34. Run1 latches total versus runs -- difference and pct
  35. Run1        Run2        Diff       Pct
  36. 49,380       2,264     -47,116  2,181.10%

二者的差异同样很明显。

不仅如此,在存储过程中,执行P1过程之后,甚至在临时表空间中产生了临时段,而这个临时段是不会自动清除的,经测试,即使没有用于返回结果的clob参数,在过程内部生成的clob所占的临时段也不会自动清除,只有会话退出才会清除掉。如果更深入跟踪,也许可以发现大量的逻辑读来源于这个临时段。

view plain copy to clipboard print ?
  1. SQL> select tablespace,contents,segtype,blocks from v$sort_usage;
  2. TABLESPACE                      CONTENTS  SEGTYPE       BLOCKS
  3. ------------------------------- --------- --------- ----------
  4. TEMP                            TEMPORARY LOB_DATA         128

通过验证,证明clob类型的数据的确是引起客户系统中存储过程大量逻辑读和Latch争用的原因,找到了这个原因,优化就相对简单了。

在PL/SQL存储过程中,clob是相当方便的一种数据类型,由于其能够存储超长字符数据的特性,使得在这种用于数据交换的存储过程中用得较多。然而,从上面的数据中,不难发现,如果大量使用clob运算,将普通的字符串拼接成clob,其CPU消耗、逻辑读、甚至是latch的获取都是非常高的,对性能影响非常大。实际上在10g中,存储过程中的varchar2类型,其长度最大可以达到32767,所以如果返回的结果确保不超过这个长度,完全可以使用varchar2类型,只是在返回时再转换为clob,正如上面的存储过程P2所做的那样。如果其长度超过了32767,也可以如存储过程P3一样,先将短小的字符串拼接成较大的字符串,然后将较大的字符串拼接到clob中。

« Hint的常见错误使用方式
修改隐含参数_library_cache_advice解决性能问题一例 »

在存储过程中的clob数据类型相关推荐

  1. java mysql nclob_java语言操作Oracle数据库中的CLOB数据类型 (转)

    java语言操作Oracle数据库中的CLOB数据类型 (转)[@more@] 有关字段类型的相关信息可以查阅oracle技术网.下面摘抄一些有关blob.clob等类型的说明.又便于大家的了解. 字 ...

  2. Oracle存储过程中使用游标来批量解析CLOB字段里面的xml字符串:

    摘要:之前在项目中使用到了Oracle数据库中通过触发器去调用存储过程执行数据解析并Update到对应的数据表中,但是,经过一段时间的测试使用发现,如果job那天停掉了,然后你再重新新建job的话,这 ...

  3. Oracle中通过游标执行带参数的存储过程实现解析CLOB字段内的xml字符串:

    摘要:近来之前的项目数据出现了问题,原因是由于之前在设计数据库的时候把时间字段设置成了字符串格式,所以给后期的数据操作带来了很大的麻烦,这里提醒一下各位程序猿,以后在开发项目的时候时间字段一定要是时间 ...

  4. PLSQLDeveloper中调试含clob字段的存储过程中出现 ORA-03127: 在活动操作结束之前不允许进行操作

    PLSQLDeveloper中调试含clob字段的存储过程中出现 ORA-03127: 在活动操作结束之前不允许进行操作,百思不得其解. 后查看英文书籍中clob讲解后突然觉悟,clob实际就是指针, ...

  5. python中int表示的数据类型是_python中的基本数据类型之 int bool str

    一.基本数据类型 1. int  ==>  整数.主要用来进行数学运算. 2.str  ==>  字符串.可以保存少量的数据,并进行相应的操作. 3.bool  =>  布尔值.判断 ...

  6. Oracle存储过程中异常Exception的捕捉和处理

    Oracle存储过程中异常的捕捉和处理 CREATE OR REPLACE Procedure Proc_error_process ( v_IN in Varchar2, v_OUT Out Var ...

  7. SQLServer 中存储过程返回的三种方式( 包括存储过程的创建, 在存储过程中调用, 在VS中调用的方法)...

    存储过程有三种返回: 1.   用return返回数字型数据 2.   用返回参数返回结果,可以返回各种数据类型(通过游标来循环查询结果每一行) 3.   直接在存储过程中用select返回结果集,可 ...

  8. 实例讲解如何把表格变量传递到存储过程中

    传递表值参数 用户经常会碰到许多需要把数值容器而非单个数值放到存储过程里的情况.对于大部分的编程语言而言,把容器数据结构传递到例程里或传递出来是很常见而且很必要的功能.TSQL也不例外. SQL Se ...

  9. Oracle存储过程中如何根据指定的参数判断该参数的值否存在数据表中:

    摘要:最近项目中用到了Oracle存储过程,所以就自己尝试着写了下,下面我把我遇到的问题描述一下:就是在我处理解析Clob字段中的xml字符串的时候,有个需求就是根据指定的主键参数,来判断该参数的值是 ...

最新文章

  1. 听法国设计师大卫·维森特讲述他与CorelDRAW的渊源
  2. 声学漫谈之七:扬声器等效电路与参数
  3. SPOJ_SUBLEX
  4. python数据类型可变和不可变_Python——可变和不可变类型数据
  5. 众多SEO专家集体盛赞
  6. 数据库中间件漫谈——看看云时代,它会走向何方
  7. 搜索算法(一)--DFS/BFS求解拯救同伴问题(JAVA)
  8. 结对编程项目---四则运算
  9. Percona XtraDB cluster--第一部分:XtraDB cluster安装 (Centos7)
  10. 激活码方式注册的实现原理述
  11. CentOS6.x安装方法超详细教程
  12. OSChina 周一乱弹 ——怎么用微微一石更表白!
  13. AutoCAD.NET实现CAD截图
  14. 新能源行业商业采购协同系统:赋能新能源行业采购业务,提升产业协同
  15. 13.歌手大赛计分程序。
  16. 光学雨量计应用降雨量检测
  17. Silvaco学习笔记(六)毕设相关
  18. C 语言字符数组的定义与初始化
  19. Python攻关之模块(2)
  20. Java中的如何检测字符串是否相等

热门文章

  1. spring security 3配置ACL时报java.lang.NoSuchMethodError: net.sf.ehcache.Cache.init
  2. 微信公众号如何开通模板消息功能?申请理由怎么写?
  3. 英国脱欧不过是小事一桩
  4. MySql查询表是否被锁
  5. Robolectric单元测试 - Android Could not determine artifacts for XXXX: Skipped due to earlier error
  6. 【权限设计】最好的权限设计,是先区分功能权限和数据权限
  7. 使用apache的ZipOutputStream进行zip文件压缩
  8. Leetcode:面试题 01.06. 字符串压缩
  9. KindEditor插件(富文本编辑器)的使用
  10. python两台电脑文件传输_两行python代码实现一个http服务器,告别文件传输烦恼...