使用dbms_profiler來觀察每一行PLSQL程式的執行時間

hooca發表於2014-10-25
使用dbms_profiler來觀察每一行PLSQL程式的執行時間
有時候我們在執行一段比較長的plsql程式的時候,發現執行過程比較慢,但又不知道是哪部分出現了效能問題,這時候應該如何快速找到最耗費時間的部分呢?當然有很多方法,但這裡推薦一種方法,可以使用dbms_profiler包來觀察每一行程式碼的執行次數和時間。
一、操作步驟
1. 登入到SYS使用者,檢查dbms_profiler包有沒有安裝。沒有的話執行$ORACLE_HOME/rdbms/admin/profload.sql來建立該包。
2. 將該包的執行許可權賦予待除錯的使用者。
3. 登入到除錯使用者,執行$ORACLE_HOME/rdbms/admin/proftab.sql建立相關表。
4. 在待優化的PL/SQL程式前後加上執行dbms_profiler包的程式碼,如下:
BEGIN
  DBMS_PROFILER.START_PROFILER('any comment');
  ...'your pl/sql code'
  DBMS_PROFILER.STOP_PROFILER;
  ...
END;
5. 執行該PL/SQL程式,然後檢查PLSQL_PROFILER_DATA表內容,可以從中看到每一行程式碼的執行次數,每行程式碼總共的執行時間。
二、具體的例子:
SQL> conn / as sysdba;
Connected.
1.檢查是否安裝了dbms_profiler包
SQL> desc dbms_profiler;
ERROR:
ORA-04043: object dbms_profiler does not exist
--如果沒有安裝的話,執行下面的語句安裝
SQL> @/disk_dev/oracle/product/9.2.0/rdbms/admin/profload.sql;
Package created.
Grant succeeded.
Synonym created.
Library created.
Package body created.
Testing for correct installation
SYS.DBMS_PROFILER successfully loaded.
PL/SQL procedure successfully completed.
2.把dbms_profiler的執行許可權賦於oauser
SQL> grant execute on dbms_profiler to oauser;
Grant succeeded.
3.使用oauser登入,執行proftab.sql,此檔案實際是建立plsql_profiler_data、plsql_profiler_units、plsql_profiler_runs三個表,具體proftab.sql的內容如下:
proftab.sql:
drop table plsql_profiler_data cascade constraints;
drop table plsql_profiler_units cascade constraints;
drop table plsql_profiler_runs cascade constraints;
drop sequence plsql_profiler_runnumber;
create table plsql_profiler_runs
(
  runid           number primary key,  -- unique run identifier,
                                       -- from plsql_profiler_runnumber
  related_run     number,              -- runid of related run (for client/
                                       --     server correlation)
  run_owner       varchar2(32),        -- user who started run
  run_date        date,                -- start time of run
  run_comment     varchar2(2047),      -- user provided comment for this run
  run_total_time  number,              -- elapsed time for this run
  run_system_info varchar2(2047),      -- currently unused
  run_comment1    varchar2(2047),      -- additional comment
  spare1          varchar2(256)        -- unused
);
 
comment on table plsql_profiler_runs is
        'Run-specific information for the PL/SQL profiler';
create table plsql_profiler_units
(
  runid              number references plsql_profiler_runs,
  unit_number        number,           -- internally generated library unit #
  unit_type          varchar2(32),     -- library unit type
  unit_owner         varchar2(32),     -- library unit owner name
  unit_name          varchar2(32),     -- library unit name
  -- timestamp on library unit, can be used to detect changes to
  -- unit between runs
  unit_timestamp     date,
  total_time         number DEFAULT 0 NOT NULL,
  spare1             number,           -- unused
  spare2             number,           -- unused
  -- 
  primary key (runid, unit_number)
);
comment on table plsql_profiler_units is
        'Information about each library unit in a run';
create table plsql_profiler_data
(
  runid           number,           -- unique (generated) run identifier
  unit_number     number,           -- internally generated library unit #
  line#           number not null,  -- line number in unit
  total_occur     number,           -- number of times line was executed
  total_time      number,           -- total time spent executing line
  min_time        number,           -- minimum execution time for this line
  max_time        number,           -- maximum execution time for this line
  spare1          number,           -- unused
  spare2          number,           -- unused
  spare3          number,           -- unused
  spare4          number,           -- unused
  --
  primary key (runid, unit_number, line#),
  foreign key (runid, unit_number) references plsql_profiler_units
);
comment on table plsql_profiler_data is
        'Accumulated data from all profiler runs';
create sequence plsql_profiler_runnumber start with 1 nocache;
SQL> conn oauser/oauser;
Connected.
SQL> @/disk_dev/oracle/product/9.2.0/rdbms/admin/proftab.sql;
4.建立一個測試的過程,在程式碼中加入dbms_profiler程式碼,如下:
create or replace procedure p_dbms_profiler_test
 is
 i number :=1 ;
 v_str varchar2(4000);
begin
  dbms_profiler.start_profiler('測試一下');
  dbms_output.put_line(i);
  for i in 1..100 loop
   exit when length(v_str)>100;
    v_str := v_str||i;
  end loop;
  dbms_output.put_line(v_str);
  dbms_profiler.stop_profiler;
end p_dbms_profiler_test;
 
SQL> exec p_dbms_profiler_test;
PL/SQL procedure successfully completed
SQL> exec p_dbms_profiler_test;
PL/SQL procedure successfully completed
SQL> exec p_dbms_profiler_test;
PL/SQL procedure successfully completed
執行一次p_dbms_profiler_test過程,就會分別在plsql_profiler_units和plsql_profiler_runs插入一條資訊,在plsql_profiler_data插入具體的過程執行資料。
5.檢視結果
通過下面的SQL語句我們可以知道每一行程式碼的執行次數,時間等等。
SQL> select t1.line#,t3.text,t1.total_occur,t1.total_time,min_time, max_time
  2  from plsql_profiler_data t1,plsql_profiler_units t2,user_source t3
  3  where t1.runid = t2.runid
  4     and t2.unit_name='P_DBMS_PROFILER_TEST'
  5     and t2.unit_name = t3.name
  6     and t1.line#=t3.line
  7     and t1.runid=3
  8  order by t1.line#
  9  /
 
LINE# TEXT                                        TOTAL_OCCUR TOTAL_TIME   MIN_TIME   MAX_TIME
---------- -------------------------------------------------------------------------------- --
    3  i number :=1 ;                                       0          0          0          0
    6   dbms_profiler.start_profiler('測試一下');           0          0          0          0
    7   dbms_output.put_line(i);                            1      17000      17000      17000
    8   for i in 1..100 loop                               56     222000       2000       6000
    9    exit when length(v_str)>100;                      56     232000       3000       7000
   10     v_str := v_str||i;                               55     254000       3000      16000
   12   dbms_output.put_line(v_str);                        1       5000       5000       5000
   13   dbms_profiler.stop_profiler;                        1       5000       5000       5000
   14 end p_dbms_profiler_test;                             0          0          0         0
9 rows selected

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22621861/viewspace-1309740/,如需轉載,請註明出處,否則將追究法律責任。

相關文章