使用 DBMS_PROFILER 定位 PL/SQL 瓶颈代码

对于SQL的优化,可以借助于SQL tuning advisor实现自动SQL优化与调整。而对于PL/SQL代码而言,既有SQL代码,又有PL/SQL代码,仅仅使用10046事件是远远不够的,因为可能SQL时间很短,而整个包或过程执行时间N久,而且包或过程中又嵌套有其他包,过程,函数。看得你头皮发麻。尽管没有工具可以直接作用于PL/SQL代码进行优化,但借助于PL/SQL PROFILER来定位你的代码块中哪些部分是性能瓶颈就已经达到了事半功倍的效果。本文首先描述了安装PROFILER,接下给出在PL/SQL块中使用字面量与绑定变量时定义瓶颈块以及对比的情形,最后部分列出一些相关脚本。
      本文描述中涉及到的相关参考
          绑定变量及其优缺点
          Oracle 硬解析与软解析
          Oracle 绑定变量窥探
          SQL Tuning Advisor(STA) 到底做了什么?
          使用SQL tuning advisor(STA)自动优化SQL
  
1、配置PROFILER及演示环境
[sql] view plain copy print ?
  1. --演示环境  
  2. sys@USBO> select * from v$version where rownum<2;  
  3.   
  4. BANNER  
  5. ------------------------------------------------------------------------------------------------------------  
  6. Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production  
  7.   
  8. a、使用需要进行plsql剖析的schema执行脚本proftab.sql(也可以使用一个账户创建然后创建同义词并授权给public)  
  9. --首次使用时执行,会创建相应的表存储profiler信息,即plsql_profiler_runs,plsql_profiler_units,plsql_profiler_data  
  10. sys@USBO> conn scott/tiger;  
  11. Connected.  
  12. scott@USBO> @?/rdbms/admin/proftab.sql  
  13.   
  14. b、使用sysdba帐户安装包DBMS_PROFILER,执行脚本profload.sql   
  15. scott@USBO> conn / as sysdba  
  16. Connected.  
  17. sys@USBO> @?/rdbms/admin/profload.sql  
  18.   
  19. c、如果需要,创建plan_table,执行脚本utlxplan.sql  
  20. sys@USBO> @?/rdbms/admin/utlxplan.sql  
  21.   
  22. sys@USBO> GRANT ALL ON sys.plan_table TO public;  
  23.   
  24. sys@USBO> CREATE PUBLIC SYNONYM plan_table FOR sys.plan_table;  
  25.   
  26. sys@USBO> conn scott/tiger;  
  27. Connected.  
  28.   
  29. --创建演示表  
  30. scott@USBO> create table t1(id number,val number);  
  31.   
  32. --创建一个基于字面量的过程  
  33. scott@USBO> create or replace procedure literals  
  34.   2  is  
  35.   3   v_num number;  
  36.   4  begin  
  37.   5   for i in 1..100000 loop  
  38.   6     v_num := dbms_random.random;                 
  39.   7     execute immediate   
  40.   8      'insert into t1 values ('||v_num||','||v_num||')';  
  41.   9   end loop;  
  42.  10   end;  
  43.  11  /  
  44.   
  45. Procedure created.  

2、使用PROFILER剖析PLSQL代码(法一)

[sql] view plain copy print ?
  1. a、启动profiler,调用过程start_profiler  
  2. scott@USBO> execute dbms_profiler.start_profiler('literals');  
  3.   
  4. b、执行你需要剖析的代码(包,过程,匿名块等)  
  5. scott@USBO> exec literals;  
  6.   
  7. c、停止profiler,调用过程stop_profiler  
  8. scott@USBO> execute dbms_profiler.stop_profiler;  
  9.   
  10. d、查看profiler报告  
  11. scott@USBO> @chk_profile  
  12. Enter value for input_comment_name: literals  
  13. Enter value for input_sp_name: literals  
  14.   
  15. TEXT                                                    TOTAL_OCCUR TOTAL_TIME MIN_TIME MAX_TIME  
  16. ------------------------------------------------------- ----------- ---------- -------- --------  
  17. procedure literals                                                1         .0       .0       .0  
  18. procedure literals                                                3         .0       .0       .0  
  19. procedure literals                                                0         .0       .0       .0  
  20.  for i in 1..100000 loop                                     100001         .2       .0       .0  
  21.    v_num := dbms_random.random;                              100000         .8       .0       .0  
  22.    execute immediate                                         100000       49.9       .0       .0  
  23.  end;                                                             1         .0       .0       .0  
  24. procedure literals                                                2         .0       .0       .0  
  25.   
  26. --上面的结果可以看出整个过程中execute immediate耗用49s中,也即是说,如果能够降低该行代码时间,则整个性能会大幅提升  

3、使用PROFILER剖析PLSQL代码(法二)

[sql] view plain copy print ?
  1. --这个方法实际也没有太多的变化,只不过将需要剖析的代码和启用profiler与停止profiler封装到一个sql中  
  2. --下面创建一个使用绑定变量的示例来进行剖析  
  3. scott@USBO> create or replace procedure binds  
  4.   2  is  
  5.   3   v_num number;  
  6.   4  begin  
  7.   5   for i in 1..100000 loop  
  8.   6     v_num := dbms_random.random;  
  9.   7     insert into t1 values (v_num,v_num);  
  10.   8   end loop;  
  11.   9  end;  
  12.  10  /  
  13.   
  14. Procedure created.  
  15.   
  16. --直接调用call_profiler.sql(该代码封装了启动profiler,停止profiler)  
  17. scott@USBO> @call_profiler  
  18. Profiler started  
  19.   
  20. PL/SQL procedure successfully completed.  
  21.   
  22. Profiler stopped  
  23.   
  24. Profiler flushed  
  25.   
  26. runid:4  
  27.   
  28. --Author : Leshami  
  29. --Blog   : http://blog.csdn.net/leshami  
  30.   
  31. --查看profiler报告  
  32. scott@USBO> @evaluate_profiler_results  
  33. Enter value for runid: 4  
  34. Enter value for name: binds  
  35. Enter value for owner: scott  
  36.   
  37.       Line      Occur        sec Text  
  38. ---------- ---------- ---------- ------------------------------------------------------------  
  39.          1          0          0 procedure binds  
  40.          2                       is  
  41.          3                        v_num number;  
  42.          4                       begin  
  43.          5     100001       .182  for i in 1..100000 loop  
  44.          6     100000       .498    v_num := dbms_random.random;  
  45.          7     100000      3.756    insert into t1 values (v_num,v_num);  
  46.          8                        end loop;  
  47.          9          1          0 end;  
  48.   
  49. rows selected.  
  50.   
  51. Code% coverage  
  52. --------------  
  53.             80  
  54.   
  55. --从上面的报告可知,当改用使用绑定变量后,原来执行insert语句的时间由49.9s下降到3.756s  
  56. --对于这个事例仅仅是演示定位瓶颈代码,并改用绑定变量以提高性能,对于其他情形,具体的如何修改瓶颈代码应具体分析  

4、示例中用到的脚本

[sql] view plain copy print ?
  1. a、chk_profile.sql  
  2. --file_name: chk_profile.sql  
  3. set linesize 190  
  4. column text format a100 wrap  
  5. column total_time format 99999.9  
  6. column min_time format 99999.9  
  7. column max_time format 99999.9  
  8. select s.text ,  
  9.        p.total_occur ,  
  10.        p.total_time/1000000000 total_time,  
  11.        p.min_time/1000000000 min_time,  
  12.        p.max_time/1000000000 max_time  
  13. from plsql_profiler_data p, user_source s, plsql_profiler_runs r  
  14. where p.line# = s.line  
  15. and   p.runid = r.runid  
  16. and   r.run_comment = '&input_comment_name'  
  17. and   s.name =upper('&input_sp_name');  
  18.   
  19.   
  20. b、call_profiler  
  21. --file_name:call_profiler.sql  
  22. SET HEAD OFF  
  23. SET PAGES 0  
  24.   
  25. SELECT DECODE (DBMS_PROFILER.start_profiler, '0''Profiler started''Profiler error'FROM DUAL;  
  26.   
  27. -------you can put you plsql code in below block------------  
  28.   
  29. begin  
  30.     binds;  
  31. end;  
  32. /  
  33. ---------------------------------------------------------------  
  34.   
  35. SELECT DECODE (DBMS_PROFILER.stop_profiler, '0''Profiler stopped''Profiler error'FROM DUAL;  
  36.   
  37. SELECT DECODE (DBMS_PROFILER.flush_data, '0''Profiler flushed''Profiler error'FROM DUAL;  
  38.   
  39. SELECT 'runid:' || plsql_profiler_runnumber.CURRVAL FROM DUAL;  
  40.   
  41. SET HEAD ON  
  42. SET PAGES 200  
  43.   
  44.   
  45. c、evaluate_profiler_results.sql   
  46. --file_name:evaluate_profiler_results.sql   
  47. undef runid  
  48. undef owner  
  49. undef name  
  50. set verify off  
  51. col text format a60 wrap  
  52. SELECT s.line "Line"  
  53.      , p.total_occur "Occur"  
  54.      , p.total_time "sec"  
  55.      , s.text "Text"  
  56. FROM   all_source s  
  57.      , (SELECT u.unit_owner  
  58.              , u.unit_name  
  59.              , u.unit_type  
  60.              , d.line#  
  61.              , d.total_occur  
  62.              , round(d.total_time / 1000000000,3) total_time  
  63.         FROM   plsql_profiler_data d, plsql_profiler_units u  
  64.         WHERE  u.runid = &&runid AND u.runid = d.runid AND u.unit_number = d.unit_number) p  
  65. WHERE      s.owner = p.unit_owner(+)  
  66.        AND s.name = p.unit_name(+)  
  67.        AND s.TYPE = p.unit_type(+)  
  68.        AND s.line = p.line#(+)  
  69.        AND s.name = UPPER ( '&&name' )  
  70.        AND s.owner = UPPER ( '&&owner' )  
  71. ORDER BY s.line;  
  72.   
  73. SELECT exec.cnt / total.cnt * 100 "Code% coverage"  
  74. FROM   (SELECT COUNT ( 1 ) cnt  
  75.         FROM   plsql_profiler_data d, plsql_profiler_units u  
  76.         WHERE      d.runid = &&runid  
  77.                AND u.runid = d.runid  
  78.                AND u.unit_number = d.unit_number  
  79.                AND u.unit_name = UPPER ( '&&name' )  
  80.                AND u.unit_owner = UPPER ( '&&owner' )) total  
  81.      , (SELECT COUNT ( 1 ) cnt  
  82.         FROM   plsql_profiler_data d, plsql_profiler_units u  
  83.         WHERE      d.runid = &&runid  
  84.                AND u.runid = d.runid  
  85.                AND u.unit_number = d.unit_number  
  86.                AND u.unit_name = UPPER ( '&&name' )  
  87.                AND u.unit_owner = UPPER ( '&&owner' )  
  88.                AND d.total_occur > 0) exec;  
  89.   
  90. undef runid  
  91. undef owner  
  92. undef name 
Forward from http://blog.csdn.net/leshami/article/details/12100235

你可能感兴趣的:(使用 DBMS_PROFILER 定位 PL/SQL 瓶颈代码)