Oracle’s DBMS_Profiler:PL/SQL 性能调整
DBMS_PROFILER 包举例
下面是我提供的怎样使用配置的简单例子,运行配置文件来测试下面例程的性能. 例程用到的自定义脚本紧随其后.
1. 创建过程.
create or replace procedure am_perf_chk (pi_seq in number,
pio_status in out nocopy varchar2) is
l_dat date := sysdate;
begin
if trunc(l_dat) = '21-sep-02' and pi_seq = 1 then
pio_status := 'OK';
else
pio_status := 'Invalid tape loaded';
end if;
exception
when others then
pio_status := 'Error in am_perf_chek';
end;
2. 用配置文件调用例程
替换上面的例程, 执行call_profiler.sql脚本(脚本代码参见下面),传入pi_seq=2
SQL> @d:\am\call_profiler.sql
Profiler started
Invalid tape loaded
PL/SQL procedure successfully completed.
Profiler stopped
Profiler flushed
runid:8
3. 评估执行时间:
执行eavluate_profiler_results.sql脚本,得到时间统计
SQL> @d:\am\evaluate_profiler_results.sql
Enter value for runid: 8
Enter value for name: am_perf_chk
Enter value for owner: scott
Line Occur Msec Text
---------- ---------- ---------- -------------------------------------------------------------------
1 procedure am_perf_chk (pi_seq in number,
2 pio_status in out nocopy varchar2) is
3 2 43.05965 l_dat date := sysdate;
4 begin
5 1 86.35732 if trunc(l_dat) = '21-sep-02' and pi_seq = 1 then
6 0 0 pio_status := 'OK';
7 else
8 1 8.416151 pio_status := 'Invalid tape loaded';
9 end if;
10 exception
11 when others then
12 0 0 pio_status := 'Error in am_perf_chek';!
13 1 2.410361 end;
13 rows selected.
Code% coverage
--------------
66.6666667
4. 正如你看到的,第三行执行时间提高到86毫秒.但是改变if语句,重新执行上面的过程,将会得到新的结果:
Line Occur Msec Text
---------- ---------- ---------- -------------------------------------------------------------------
1 procedure am_perf_chk (pi_seq in number,
2 pio_status in out nocopy varchar2) is
3 2 17.978816 l_dat date := sysdate;
4 begin
5 1 8.419503 if pi_seq = 1 and trunc(l_dat) = '21-sep-02' then
6 0 0 pio_status := 'OK';
7 else
8 1 7.512684 pio_status := 'Invalid tape loaded';
9 end if;
10 exception
11 when others then
12 0 0 pio_status := 'Error in !am_perf_chek';
13 1 .731657 end;
13 rows selected.
Code% coverage
--------------
66.6666667
5. 正如你看到的, 这种情境下第三行执行时间从86毫秒减少到8毫秒,多余的时间是由于内置trunc()函数引起., 这种情境下如果第一个条件为false,则不会执行trunc()函数.这仅仅是个简单的例子,当你测试的例程越大,你面临的挑战更大.
这个配置结果也证明了执行期间代码被覆盖多少行,从而让我们知道处于性能监视中的代码范围。如果任何PL/SQL块性能出现问题,它也能提炼出各种不同情景的正在在执行的代码并检查配置结果,从而查明问题所在。
6. 对于一个特定的情景,如果执行一段特殊的代码段,可以得到合理的分析,即使代码根本一点都不能运行。
环境的创建
默认安装或数据库的创建状态下,DBMS_PROFILER包不会自动安装,请DBA用profload.sql脚本创建它.用一个权限较大的或一个单独的用户,创建存储统计信息的表。如果
用如SYS用户创建,则给其它用户授予DML权限,并且对这些表创建一个共同的简写名.
创建表的如下:
PLSQL_PROFILER_RUNS表:PL/SQL配置的运行细节.
PLSQL_PROFILER_UNITS表:运行中每一个库单元的信息.
PLSQL_PROFILER_DATA表:所有配置文件运行时的数据累积.
PLSQL_PROFILER_RUNNUMBER序列提供了RUNID
运行和解释配置数据
ORACLE提供了三个表来统计,填充RUNID。有许多第三方的工具可以提供自定义的基于这些数据的报告,ORACLE提供profrep.sql脚本评估数据(在<oracle_home>\plsql\demo\目录下),下面的两个简单脚本就是上面用到的,用来检查程序单元的执行时间.执行时间以毫秒存储
-----------------------------------------------------------
Script: call_profiler.sql
-----------------------------------------------------------
set head off
set pages 0
select decode(dbms_profiler.start_profiler, '0', 'Profiler started', 'Profiler error')
from dual;
--< place your routine in the below block >--
declare
l_status varchar2(200);
begin
am_perf_chk(2, l_status);
dbms_output.put_line(l_status);
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
-----------------------------------------------------------
Script: evaluate_profiler_results.sql
-----------------------------------------------------------
undef runid
undef owner
undef name
set verify off
select s.line "Line", p.total_occur "Occur", p.total_time "Msec", s.text "Text"
from all_source s, (select u.unit_owner, u.unit_name, u.unit_type, d.line#,
d.total_occur, d.total_time/1000000 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
结论
DBMS_PROFILER是非常强大的工具,其一就是可以识别PL/SQL的性能问题.这个工具最好用在开发时期,用来调整基于各种应用的情景的代码,它也能用很好的调整已在使用中的例程并且采取显而易见的时间去执行。总之,这个工具可以给每一行代码给予性能统计,它可以帮助我们评估和调整到一个出色的水平,当检查SQL语句的性能问题时,PL/SQL代码不应该忽略,相反应该调整到最佳的结果.