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

简介:       对于SQL的优化,可以借助于SQL tuning advisor实现自动SQL优化与调整。而对于PL/SQL代码而言,既有SQL代码,又有PL/SQL代码,仅仅使用10046事件是远远不够的,因为可能SQL时间很短,而整个包或过程执行时间N久,而且包或过程中又嵌套有其他包,过程,函数。

      对于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及演示环境

--演示环境
sys@USBO> select * from v$version where rownum<2;

BANNER
------------------------------------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

a、使用需要进行plsql剖析的schema执行脚本proftab.sql(也可以使用一个账户创建然后创建同义词并授权给public)
--首次使用时执行,会创建相应的表存储profiler信息,即plsql_profiler_runs,plsql_profiler_units,plsql_profiler_data
sys@USBO> conn scott/tiger;
Connected.
scott@USBO> @?/rdbms/admin/proftab.sql

b、使用sysdba帐户安装包DBMS_PROFILER,执行脚本profload.sql 
scott@USBO> conn / as sysdba
Connected.
sys@USBO> @?/rdbms/admin/profload.sql

c、如果需要,创建plan_table,执行脚本utlxplan.sql
sys@USBO> @?/rdbms/admin/utlxplan.sql

sys@USBO> GRANT ALL ON sys.plan_table TO public;

sys@USBO> CREATE PUBLIC SYNONYM plan_table FOR sys.plan_table;

sys@USBO> conn scott/tiger;
Connected.

--创建演示表
scott@USBO> create table t1(id number,val number);

--创建一个基于字面量的过程
scott@USBO> create or replace procedure literals
  2  is
  3   v_num number;
  4  begin
  5   for i in 1..100000 loop
  6     v_num := dbms_random.random;               
  7     execute immediate 
  8      'insert into t1 values ('||v_num||','||v_num||')';
  9   end loop;
 10   end;
 11  /

Procedure created.

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

a、启动profiler,调用过程start_profiler
scott@USBO> execute dbms_profiler.start_profiler('literals');

b、执行你需要剖析的代码(包,过程,匿名块等)
scott@USBO> exec literals;

c、停止profiler,调用过程stop_profiler
scott@USBO> execute dbms_profiler.stop_profiler;

d、查看profiler报告
scott@USBO> @chk_profile
Enter value for input_comment_name: literals
Enter value for input_sp_name: literals

TEXT                                                    TOTAL_OCCUR TOTAL_TIME MIN_TIME MAX_TIME
------------------------------------------------------- ----------- ---------- -------- --------
procedure literals                                                1         .0       .0       .0
procedure literals                                                3         .0       .0       .0
procedure literals                                                0         .0       .0       .0
 for i in 1..100000 loop                                     100001         .2       .0       .0
   v_num := dbms_random.random;                              100000         .8       .0       .0
   execute immediate                                         100000       49.9       .0       .0
 end;                                                             1         .0       .0       .0
procedure literals                                                2         .0       .0       .0

--上面的结果可以看出整个过程中execute immediate耗用49s中,也即是说,如果能够降低该行代码时间,则整个性能会大幅提升

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

--这个方法实际也没有太多的变化,只不过将需要剖析的代码和启用profiler与停止profiler封装到一个sql中
--下面创建一个使用绑定变量的示例来进行剖析
scott@USBO> create or replace procedure binds
  2  is
  3   v_num number;
  4  begin
  5   for i in 1..100000 loop
  6     v_num := dbms_random.random;
  7     insert into t1 values (v_num,v_num);
  8   end loop;
  9  end;
 10  /

Procedure created.

--直接调用call_profiler.sql(该代码封装了启动profiler,停止profiler)
scott@USBO> @call_profiler
Profiler started

PL/SQL procedure successfully completed.

Profiler stopped

Profiler flushed

runid:4

--Author : Leshami
--Blog   : http://blog.csdn.net/leshami

--查看profiler报告
scott@USBO> @evaluate_profiler_results
Enter value for runid: 4
Enter value for name: binds
Enter value for owner: scott

      Line      Occur        sec Text
---------- ---------- ---------- ------------------------------------------------------------
         1          0          0 procedure binds
         2                       is
         3                        v_num number;
         4                       begin
         5     100001       .182  for i in 1..100000 loop
         6     100000       .498    v_num := dbms_random.random;
         7     100000      3.756    insert into t1 values (v_num,v_num);
         8                        end loop;
         9          1          0 end;

9 rows selected.

Code% coverage
--------------
            80

--从上面的报告可知,当改用使用绑定变量后,原来执行insert语句的时间由49.9s下降到3.756s
--对于这个事例仅仅是演示定位瓶颈代码,并改用绑定变量以提高性能,对于其他情形,具体的如何修改瓶颈代码应具体分析

4、示例中用到的脚本

a、chk_profile.sql
--file_name: chk_profile.sql
set linesize 190
column text format a100 wrap
column total_time format 99999.9
column min_time format 99999.9
column max_time format 99999.9
select s.text ,
       p.total_occur ,
       p.total_time/1000000000 total_time,
       p.min_time/1000000000 min_time,
       p.max_time/1000000000 max_time
from plsql_profiler_data p, user_source s, plsql_profiler_runs r
where p.line# = s.line
and   p.runid = r.runid
and   r.run_comment = '&input_comment_name'
and   s.name =upper('&input_sp_name');


b、call_profiler
--file_name:call_profiler.sql
SET HEAD OFF
SET PAGES 0

SELECT DECODE (DBMS_PROFILER.start_profiler, '0', 'Profiler started', 'Profiler error') FROM DUAL;

-------you can put you plsql code in below block------------

begin
    binds;
end;
/
---------------------------------------------------------------

SELECT DECODE (DBMS_PROFILER.stop_profiler, '0', 'Profiler stopped', 'Profiler error') FROM DUAL;

SELECT DECODE (DBMS_PROFILER.flush_data, '0', 'Profiler flushed', 'Profiler error') FROM DUAL;

SELECT 'runid:' || plsql_profiler_runnumber.CURRVAL FROM DUAL;

SET HEAD ON
SET PAGES 200


c、evaluate_profiler_results.sql 
--file_name:evaluate_profiler_results.sql 
undef runid
undef owner
undef name
set verify off
col text format a60 wrap
SELECT s.line "Line"
     , p.total_occur "Occur"
     , p.total_time "sec"
     , s.text "Text"
FROM   all_source s
     , (SELECT u.unit_owner
             , u.unit_name
             , u.unit_type
             , d.line#
             , d.total_occur
             , round(d.total_time / 1000000000,3) total_time
        FROM   plsql_profiler_data d, plsql_profiler_units u
        WHERE  u.runid = &&runid AND u.runid = d.runid AND u.unit_number = d.unit_number) p
WHERE      s.owner = p.unit_owner(+)
       AND s.name = p.unit_name(+)
       AND s.TYPE = p.unit_type(+)
       AND s.line = p.line#(+)
       AND s.name = UPPER ( '&&name' )
       AND s.owner = UPPER ( '&&owner' )
ORDER BY s.line;

SELECT exec.cnt / total.cnt * 100 "Code% coverage"
FROM   (SELECT COUNT ( 1 ) cnt
        FROM   plsql_profiler_data d, plsql_profiler_units u
        WHERE      d.runid = &&runid
               AND u.runid = d.runid
               AND u.unit_number = d.unit_number
               AND u.unit_name = UPPER ( '&&name' )
               AND u.unit_owner = UPPER ( '&&owner' )) total
     , (SELECT COUNT ( 1 ) cnt
        FROM   plsql_profiler_data d, plsql_profiler_units u
        WHERE      d.runid = &&runid
               AND u.runid = d.runid
               AND u.unit_number = d.unit_number
               AND u.unit_name = UPPER ( '&&name' )
               AND u.unit_owner = UPPER ( '&&owner' )
               AND d.total_occur > 0) exec;

undef runid
undef owner
undef name

 

Oracle&nbsp;牛鹏社    Oracle DBsupport

更多参考

DML Error Logging 特性 

PL/SQL --> 游标

PL/SQL --> 隐式游标(SQL%FOUND)

批量SQL之 FORALL 语句

批量SQL之 BULK COLLECT 子句

PL/SQL 集合的初始化与赋值

PL/SQL 联合数组与嵌套表
PL/SQL 变长数组
PL/SQL --> PL/SQL记录

SQL tuning 步骤

高效SQL语句必杀技

父游标、子游标及共享游标

绑定变量及其优缺点

dbms_xplan之display_cursor函数的使用

dbms_xplan之display函数的使用

执行计划中各字段各模块描述

使用 EXPLAIN PLAN 获取SQL语句执行计划

目录
相关文章
|
11天前
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
127 11
|
2月前
|
SQL 数据采集 监控
局域网监控电脑屏幕软件:PL/SQL 实现的数据库关联监控
在当今网络环境中,基于PL/SQL的局域网监控系统对于企业和机构的信息安全至关重要。该系统包括屏幕数据采集、数据处理与分析、数据库关联与存储三个核心模块,能够提供全面而准确的监控信息,帮助管理者有效监督局域网内的电脑使用情况。
45 2
|
3月前
|
SQL 关系型数据库 MySQL
创建SQL数据库的基本步骤与代码指南
在信息时代,数据管理显得尤为重要,其中数据库系统已成为信息技术架构的关键部分。而当我们谈论数据库系统时,SQL(结构化查询语言)无疑是其中最核心的工具之一。本文将详细介绍如何使用SQL创建数据库,包括编写相应的代码和必要的步骤。由于篇幅限制,本文可能无法达到您要求的2000字长度,但会尽量涵盖创建数
156 3
|
3月前
|
SQL 监控 关系型数据库
SQL错误代码1303解析与处理方法
在SQL编程和数据库管理中,遇到错误代码是常有的事,其中错误代码1303在不同数据库系统中可能代表不同的含义
|
3月前
|
SQL 安全 关系型数据库
SQL错误代码1303解析与解决方案:深入理解并应对权限问题
在数据库管理和开发过程中,遇到错误代码是常见的事情,每个错误代码都代表着一种特定的问题
|
5月前
|
存储 SQL 安全
【数据库高手的秘密武器:深度解析SQL视图与存储过程的魅力——封装复杂逻辑,实现代码高复用性的终极指南】
【8月更文挑战第31天】本文通过具体代码示例介绍 SQL 视图与存储过程的创建及应用优势。视图作为虚拟表,可简化复杂查询并提升代码可维护性;存储过程则预编译 SQL 语句,支持复杂逻辑与事务处理,增强代码复用性和安全性。通过创建视图 `high_earners` 和存储过程 `get_employee_details` 及 `update_salary` 的实例,展示了二者在实际项目中的强大功能。
55 1
|
4月前
|
SQL 分布式计算 大数据
大数据开发SQL代码编码原则和规范
这段SQL编码原则强调代码的功能完整性、清晰度、执行效率及可读性,通过统一关键词大小写、缩进量以及禁止使用模糊操作如select *等手段提升代码质量。此外,SQL编码规范还详细规定了代码头部信息、字段与子句排列、运算符前后间隔、CASE语句编写、查询嵌套、表别名定义以及SQL注释的具体要求,确保代码的一致性和维护性。
146 0
|
4月前
|
SQL 安全 数据库
基于SQL Server事务日志的数据库恢复技术及实战代码详解
基于事务日志的数据库恢复技术是SQL Server中一个非常强大的功能,它能够帮助数据库管理员在数据丢失或损坏的情况下,有效地恢复数据。通过定期备份数据库和事务日志,并在需要时按照正确的步骤恢复,可以最大限度地减少数据丢失的风险。需要注意的是,恢复数据是一个需要谨慎操作的过程,建议在执行恢复操作之前,详细了解相关的操作步骤和注意事项,以确保数据的安全和完整。
235 0
|
5月前
|
JSON 数据格式 Java
化繁为简的魔法:Struts 2 与 JSON 联手打造超流畅数据交换体验,让应用飞起来!
【8月更文挑战第31天】在现代 Web 开发中,JSON 成为数据交换的主流格式,以其轻量、易读和易解析的特点受到青睐。Struts 2 内置对 JSON 的支持,结合 Jackson 库可便捷实现数据传输。本文通过具体示例展示了如何在 Struts 2 中进行 JSON 数据的序列化与反序列化,并结合 AJAX 技术提升 Web 应用的响应速度和用户体验。
157 0
|
5月前
|
Java Apache Android开发
Struts 2的秘密武器:揭秘社区中隐藏的学习宝藏,让你从新手到高手的不归路!
【8月更文挑战第31天】Struts 2学习资源丰富,除官方文档外,TutorialsPoint和W3Schools等网站提供详尽教程;《Apache Struts 2实战》等书籍含全面实例。Udemy、Pluralsight及YouTube上视频课程众多,Apache Software Foundation亦有网络研讨会。实践方面,GitHub上的开源项目及个人小项目都是好选择。寻求帮助可访问Apache官方论坛、Stack Overflow等平台。
51 0