Oracle PL/SQL 性能诊断

Oracle PL/SQL 性能诊断

适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

PL/SQL 性能诊断工具[1]:

  • DBMS_PROFILER
  • DBMS_HPROF
  • DBMS_TRACE
  • v$ 视图

2. DBMS_PROFILER

2.1 安装

@?/rdbms/admin/proftab.sql
@?/rdbms/admin/dbmsppr.sql

2.2 使用

BEGIN
  DBMS_PROFILER.START_PROFILER('my_run');
  
  -- 执行 PL/SQL
  my_proc;
  
  DBMS_PROFILER.STOP_PROFILER;
END;
/

2.3 查看结果

-- 运行
SELECT runid, run_owner, run_date FROM plsql_profiler_runs;

-- 单元
SELECT unit_number, unit_type, unit_name FROM plsql_profiler_units WHERE runid = 1;

-- 行
SELECT 
  u.unit_name,
  d.line#,
  d.total_occur,
  d.total_time,
  s.text
FROM plsql_profiler_data d
JOIN plsql_profiler_units u ON d.runid = u.runid AND d.unit_number = u.unit_number
LEFT JOIN user_source s ON u.unit_name = s.name AND d.line# = s.line
WHERE d.runid = 1
ORDER BY d.total_time DESC;

3. DBMS_HPROF(11g+)

3.1 创建目录

CREATE DIRECTORY prof_dir AS '/u01/prof';

3.2 启用

BEGIN
  DBMS_HPROF.START_PROFILING('PROF_DIR', 'prof.txt');
  
  -- 执行 PL/SQL
  my_proc;
  
  DBMS_HPROF.STOP_PROFILING;
END;
/

3.3 分析

-- 创建分析表
@?/rdbms/admin/dbmshptab.sql

-- 分析
BEGIN
  DBMS_HPROF.ANALYZE('PROF_DIR', 'prof.txt');
END;
/

-- 查看
SELECT * FROM dbmshp_function_info ORDER BY function_elapsed_time DESC;

SELECT * FROM dbmshp_parent_child_info;

3.4 优势

  • 层次化
  • 函数调用树
  • 详细时间

4. DBMS_TRACE

4.1 启用

ALTER SESSION SET PLSQL_DEBUG = TRUE;

BEGIN
  DBMS_TRACE.SET_PLSQL_TRACE(DBMS_TRACE.TRACE_ALL_CALLS);
  -- DBMS_TRACE.TRACE_ALL_SQL
  -- DBMS_TRACE.TRACE_ALL_EXCEPTIONS
  
  -- 执行
  my_proc;
  
  DBMS_TRACE.CLEAR_PLSQL_TRACE;
END;
/

4.2 查看 trace

# trace 文件
ls $ORACLE_BASE/diag/rdbms/$DB_UNIQUE_NAME/$ORACLE_SID/trace/*.trc

5. PL/SQL 性能问题

5.1 慢查询

-- 1. 在 PL/SQL 中找慢 SQL
SELECT sql_id, elapsed_time, executions
FROM v$sql
WHERE parsing_schema_name = 'SCOTT'
ORDER BY elapsed_time DESC;

5.2 循环开销

-- 慢:循环 SQL
FOR rec IN cur LOOP
  UPDATE ... WHERE id = rec.id;
END LOOP;

-- 快:FORALL
FORALL i IN 1..v_ids.COUNT
  UPDATE ... WHERE id = v_ids(i);

5.3 上下文切换

-- 减少 SQL/PL/SQL 切换
-- BULK COLLECT + FORALL

5.4 内存

-- 集合过大
-- LIMIT 限制
FETCH c BULK COLLECT INTO v LIMIT 1000;

6. 诊断脚本

6.1 测量时间

DECLARE
  v_start NUMBER;
  v_end NUMBER;
BEGIN
  v_start := DBMS_UTILITY.GET_TIME;
  
  -- 代码
  
  v_end := DBMS_UTILITY.GET_TIME;
  DBMS_OUTPUT.PUT_LINE('Elapsed: ' || (v_end - v_start) || ' hsecs');
END;

6.2 测量 CPU

DECLARE
  v_cpu_start NUMBER;
  v_cpu_end NUMBER;
BEGIN
  v_cpu_start := DBMS_UTILITY.GET_CPU_TIME;
  
  -- 代码
  
  v_cpu_end := DBMS_UTILITY.GET_CPU_TIME;
  DBMS_OUTPUT.PUT_LINE('CPU: ' || (v_cpu_end - v_cpu_start) || ' cs');
END;

7. 优化点

7.1 BULK COLLECT + FORALL

-- 批量
SELECT ... BULK COLLECT INTO v LIMIT 1000;
FORALL i IN 1..v.COUNT
  INSERT INTO ... VALUES v(i);

7.2 NOCOPY

PROCEDURE process(p_data IN OUT NOCOPY BIG_TYPE) IS ...

7.3 绑定变量

EXECUTE IMMEDIATE 'SELECT ... WHERE id = :1' USING v_id;

7.4 PLS_INTEGER

v_count PLS_INTEGER := 0;

7.5 NATIVE 编译

ALTER SYSTEM SET plsql_code_type = NATIVE SCOPE=SPFILE;

详细见:Oracle PL/SQL 性能优化


8. 内存分析

8.1 PL/SQL 内存

SELECT 
  name, 
  value / 1024 / 1024 AS mb
FROM v$mystat s, v$statname n
WHERE s.statistic# = n.statistic#
  AND n.name LIKE '%PL/SQL%';

8.2 集合内存

-- 监控集合大小
-- 避免过大

9. AWR 中的 PL/SQL

-- Top PL/SQL
SELECT 
  plsql_entry_object_id,
  plsql_entry_subprogram_id,
  COUNT(*) AS executions
FROM dba_hist_active_sess_history
WHERE snap_id BETWEEN 100 AND 110
  AND plsql_entry_object_id IS NOT NULL
GROUP BY plsql_entry_object_id, plsql_entry_subprogram_id
ORDER BY executions DESC;

10. 常见坑与排错

10.1 慢代码定位

-- 1. DBMS_HPROF
-- 2. 找热点函数
-- 3. 优化

10.2 内存泄漏

-- 1. 集合未释放
-- 2. 临时 LOB 未释放
-- 3. 监控

10.3 上下文切换多

-- 1. BULK
-- 2. 减少 SQL
-- 3. 集合操作

11. 最佳实践

  1. DBMS_HPROF 定位:精准
  2. BULK COLLECT + FORALL:性能
  3. 绑定变量:减少解析
  4. NOCOPY 大集合:避免复制
  5. PLS_INTEGER:整数运算
  6. NATIVE 编译:高性能
  7. 循环外 SQL:减少切换
  8. LIMIT 内存控制:避免过大
  9. 定期测量:基线
  10. 优化热点:效益

12. 参考资料

[1] Oracle Database PL/SQL Language Reference 19c, “Profiling PL/SQL” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-optimization-and-tuning.html