Oracle利用 DBMS_PROFILER 包来分析PL/SQL瓶颈
在 Oracle 10g 版本之前,默认情况下不会安装 DBMS_PROFILER 包,此次演示版本为11.2.0.4.0,默认已经安装。
安装 DBMS_PROFILER 包方式如下:
sqlplus / as sysdba
desc dbms_profiler --不存在则通过下面的方式创建
@?/rdbms/admin/profload.sql
另外,DBMS_PROFILER 包收集的分析信息存储在多个表中,而这些表默认情况下不会创建,需要在使用 DBMS_PROFIER 包之前在所有数据库版本中显式创建。
方法是运行位于 $ORACLE_HOME/rdbms/admin 文件夹中的脚本“proftab.sql”。这些表可以在普通用户或 SYS 用户中创建,如果在 SYS 中创建,则需要向想要使用 DBMS_PROFILER 包的用户授予适当的权限。
运行“proftab.sql”创建以下对象:
- PLSQL_PROFILER_RUNS - PL/SQL 探查器的运行特定信息。
- PLSQL_PROFILER_UNITS - 有关运行中每个库单元的信息。
- PLSQL_PROFILER_DATA - 所有分析器运行的累积数据。
- 序列 PLSQL_PROFILER_RUNNUMBER
这里使用 SYS 用户创建并授权:
sqlplus / as sysdba
@?/rdbms/admin/proftab.sql
GRANT SELECT ON plsql_profiler_runnumber TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON sys.plsql_profiler_data TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON sys.plsql_profiler_units TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON sys.plsql_profiler_runs TO PUBLIC;
CREATE PUBLIC SYNONYM sys.plsql_profiler_runnumber FOR plsql_profiler_runnumber;
CREATE PUBLIC SYNONYM sys.plsql_profiler_runs FOR plsql_profiler_runs;
CREATE PUBLIC SYNONYM sys.plsql_profiler_units FOR plsql_profiler_units;
CREATE PUBLIC SYNONYM sys.plsql_profiler_data FOR plsql_profiler_data;
https://www.cnblogs.com/PiscesCanon/p/17684336.html
这样所有的普通用户都有权限操作以上涉及的4个对象。
接下来使用普通用户ZKM进行测试,创建测试表和存储过程如下:
CREATE TABLE zkm (id int);
CREATE OR REPLACE procedure proc_insert_zkm
as
begin
for x in 1..10000
loop
insert into zkm values(x);
commit;
end loop;
end proc_insert_zkm;
/
开始测试:
1.
SET SERVEROUTPUT ON
DECLARE
v_run_id PLS_INTEGER;
v_ret_value PLS_INTEGER;
BEGIN
v_ret_value := DBMS_PROFILER.START_PROFILER('TEST PROFILER','SAMPLE PROFILER',v_run_id);
DBMS_OUTPUT.PUT_LINE ('The run_number is : ' || v_run_id);
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error while starting the profiler :' || SQLERRM);
END;
/
2.
EXEC proc_insert_zkm; --你需要调试的SQL、存储过程,匿名块等
3.
BEGIN
DBMS_PROFILER.STOP_PROFILER;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error while stopping the profiler :' || SQLERRM);
END;
/
同个会话执行:
---step 1
10:54:50 ZKM@zkmdb(26)> SET SERVEROUTPUT ON
10:54:57 ZKM@zkmdb(26)> DECLARE
10:54:57 2 v_run_id PLS_INTEGER;
10:54:57 3 v_ret_value PLS_INTEGER;
10:54:57 4 BEGIN
10:54:57 5 v_ret_value := DBMS_PROFILER.START_PROFILER('TEST PROFILER','SAMPLE PROFILER',v_run_id);
10:54:57 6 DBMS_OUTPUT.PUT_LINE ('The run_number is : ' || v_run_id);
10:54:57 7 EXCEPTION WHEN OTHERS THEN
10:54:57 8 DBMS_OUTPUT.PUT_LINE('Error while starting the profiler :' || SQLERRM);
10:54:57 9 END;
10:54:57 10 /
The run_number is : 4
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00
---step 2
10:55:00 ZKM@zkmdb(26)> EXEC proc_insert_zkm;
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.96
---step 3
10:55:11 ZKM@zkmdb(26)> BEGIN
10:55:14 2 DBMS_PROFILER.STOP_PROFILER;
10:55:14 3 EXCEPTION WHEN OTHERS THEN
10:55:14 4 DBMS_OUTPUT.PUT_LINE('Error while stopping the profiler :' || SQLERRM);
10:55:14 5 END;
10:55:14 6 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
可以查询视图PLSQL_PROFILER_RUNS来查看执行过的DBMS_PROFILER来调试的信息:
10:59:53 SYS@zkmdb(503)> COL RUN_COMMENT FOR A25
11:00:08 SYS@zkmdb(503)> SELECT RUNID, RUN_DATE, RUN_COMMENT FROM PLSQL_PROFILER_RUNS;
RUNID RUN_DATE RUN_COMMENT
---------- ------------------- -------------------------
1 2023-09-07 10:37:38 TEST PROFILER
2 2023-09-07 10:38:15 TEST PROFILER
3 2023-09-07 10:46:52 TEST PROFILER
4 2023-09-07 10:47:59 TEST PROFILER
5 2023-09-07 10:54:20 TEST PROFILER
Elapsed: 00:00:00.00
通过上边step 1步骤获得的run_number为4,结合一下脚本可以查询到我们需要的信息:
COLUMN UNIT_NAME FORMAT A35
COLUMN OCCURRED FORMAT 999999
COLUMN LINE FORMAT 9999
COLUMN TOT_TIME FORMAT 999.999999
COLUMN TEXT FORMAT A46
SELECT P.UNIT_NAME, P.OCCURRED, P.TOT_TIME, P.MIN_TIME, P.MAX_TIME, P.LINE# LINE, SUBSTR(S.TEXT, 1,75) TEXT
FROM (SELECT U.UNIT_NAME, D.TOTAL_OCCUR OCCURRED, (D.TOTAL_TIME/1E9) TOT_TIME, D.MIN_TIME/1E9 MIN_TIME, D.MAX_TIME/1E9 MAX_TIME,
D.LINE# FROM PLSQL_PROFILER_UNITS U, PLSQL_PROFILER_DATA D
WHERE D.RUNID=U.RUNID AND D.UNIT_NUMBER = U.UNIT_NUMBER AND D.TOTAL_OCCUR >0
AND U.RUNID= 4) P,USER_SOURCE S
WHERE P.UNIT_NAME = S.NAME(+) AND P.LINE# = S.LINE (+)
ORDER BY P.LINE#;
10:59:27 ZKM@zkmdb(26)> COLUMN UNIT_NAME FORMAT A35
11:03:00 ZKM@zkmdb(26)> COLUMN OCCURRED FORMAT 999999
11:03:00 ZKM@zkmdb(26)> COLUMN LINE FORMAT 9999
11:03:00 ZKM@zkmdb(26)> COLUMN TOT_TIME FORMAT 999.999999
11:03:00 ZKM@zkmdb(26)> COLUMN TEXT FORMAT A46
11:03:01 ZKM@zkmdb(26)> SELECT P.UNIT_NAME, P.OCCURRED, P.TOT_TIME, P.MIN_TIME, P.MAX_TIME, P.LINE# LINE, SUBSTR(S.TEXT, 1,75) TEXT
11:03:01 2 FROM (SELECT U.UNIT_NAME, D.TOTAL_OCCUR OCCURRED, (D.TOTAL_TIME/1E9) TOT_TIME, D.MIN_TIME/1E9 MIN_TIME, D.MAX_TIME/1E9 MAX_TIME,
11:03:01 3 D.LINE# FROM PLSQL_PROFILER_UNITS U, PLSQL_PROFILER_DATA D
11:03:01 4 WHERE D.RUNID=U.RUNID AND D.UNIT_NUMBER = U.UNIT_NUMBER AND D.TOTAL_OCCUR >0
11:03:01 5 AND U.RUNID= 4) P,USER_SOURCE S
11:03:01 6 WHERE P.UNIT_NAME = S.NAME(+) AND P.LINE# = S.LINE (+)
11:03:01 7 ORDER BY P.LINE#;
UNIT_NAME OCCURRED TOT_TIME MIN_TIME MAX_TIME LINE TEXT
----------------------------------- -------- ----------- ---------- ---------- ----- ----------------------------------------------
<anonymous> 3 .000028 .000001 .000024 1
<anonymous> 2 .000086 .000003 .000038 1
<anonymous> 2 .000116 .000004 .00004 1
<anonymous> 2 .000065 0 .000063 1
<anonymous> 2 .000094 .000004 .000041 1
<anonymous> 1 .000004 .000004 .000004 1
<anonymous> 2 .000063 .000001 .000062 2
PROC_INSERT_ZKM 10001 .001925 0 .000016 4 for x in 1..10000
<anonymous> 1 .000037 .000037 .000037 5
<anonymous> 1 .000043 .000043 .000043 6
<anonymous> 1 .000041 .000041 .000041 6
PROC_INSERT_ZKM 10000 .571903 .000049 .001052005 6 insert into zkm values(x);
PROC_INSERT_ZKM 10000 .383340 .000033 .000887004 7 commit;
PROC_INSERT_ZKM 1 .000002 .000002 .000002 9 end proc_insert_zkm;
<anonymous> 1 .000001 .000001 .000001 9
<anonymous> 1 .000001 .000001 .000001 11
16 rows selected.
Elapsed: 00:00:00.06
OCCURRED:当前line被执行的次数
TOT_TIME:执行line总共消耗时间,单位s
MIN_TIME:该line在执行了occurred次中某一次的最短时间,单位s
MAX_TIME:该line在执行了occurred次中某一次的最长时间,单位s
PS:可自行修改脚本调整时间单位,PLSQL_PROFILER_DATA.MAX_TIME单位是纳秒。
参考:
https://cloud.tencent.com/developer/article/1431556?from=15425
Using DBMS_PROFILER (文档 ID 97270.1)
防偷防爬:https://www.cnblogs.com/PiscesCanon/p/17684336.html
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_PROFILER.html