AWR报告不统计存储过程自身资源消耗,只捕获其内部SQL;定位需结合“SQL ordered by CPU Time/Physical Reads”、DBA_HIST_ACTIVE_SESS_HISTORY反查PL/SQL上下文及DBA_SOURCE文本匹配。
awr 报告本身不直接统计存储过程的资源消耗,你永远找不到名为 pkg_order.calc_total 的条目出现在 “sql ordered by cpu time” 或 “sql ordered by reads” 里——这不是报告漏了,而是 oracle 底层设计如此。
SQL ordered by CPU Time 和 SQL ordered by Physical Reads 是唯二可用入口
PL/SQL 存储过程不产生物理 I/O、不单独计时、不生成独立等待事件,它只是 SQL 的“调用容器”。真正被 AWR 捕获并归因的,永远是它内部执行的每一条 SELECT、UPDATE、INSERT 或 DELETE。
- 若过程体中含
SELECT /+ FULL(t) / COUNT(*) FROM orders t WHERE status = :b1,这条语句会以独立SQL_ID出现在 “SQL ordered by Physical Reads” 中 - 若过程循环调用
EXECUTE IMMEDIATE 'UPDATE log_tab SET ts = SYSDATE WHERE id = ' || v_id,每次拼接都可能生成新SQL_ID,导致高 CPU 分散在多条记录里 - 若过程只做纯计算(如字符串拼接、数值累加),不触发任何 SQL,则它在 AWR 中完全不可见——
v$session里也看不到对应sql_id
所以别搜过程名,先盯住这些列:
-
CPU Time (s):单次执行 > 500ms 且Executions> 100 的 SQL,大概率来自高频 PL/SQL 调用 -
Physical Reads Per Exec:> 10000 表示单次读块极多,常对应未走索引的全表扫描 -
Buffer Gets Per Exec:> 200000 但Rows Processed Per Exec很低,说明逻辑读效率差,可能是嵌套循环未驱动索引
用 DBA_HIST_ACTIVE_SESS_HISTORY 反查 PL/SQL 上下文
这是唯一能回溯“谁调用了它”的历史视图。DBA_HIST_ACTIVE_SESS_HISTORY 中的 PLSQL_ENTRY_OBJECT_ID 和 PLSQL_ENTRY_SUBPROGRAM_ID 字段,在快照周期内被采样记录(默认每秒一次,保留策略取决于 DBA_HIST_WR_CONTROL)。
执行这个查询可定位源头:
SELECT h.sample_time, o.object_name proc_name, o.object_type,
h.sql_id, h.event, h.session_state
FROM dba_hist_active_sess_history h
JOIN dba_objects o ON h.PLSQL_ENTRY_OBJECT_ID = o.object_id
WHERE h.sql_id IN (
SELECT sql_id FROM dba_hist_sqlstat
WHERE cpu_time_delta > 10000000 -- 超过10秒CPU累计
)
AND h.sample_time > SYSDATE - 1/24
ORDER BY h.sample_time DESC;
注意:
-
PLSQL_ENTRY_OBJECT_ID对应的是过程/函数定义所在的对象(如 PACKAGE BODY),不是调用点 - 若过程被自治事务或递归调用,
PLSQL_ENTRY_SUBPROGRAM_ID可能为 NULL,需结合PLSQL_ENTRY_OBJECT_ID+sql_id组合判断 - RAC 环境下必须指定
instance_number过滤,否则可能混入其他节点数据
DBA_SOURCE 全文搜索匹配高消耗 SQL 文本
AWR 里看到的 SQL 文本常被截断、标准化(如字面量转为绑定变量),而 DBA_SOURCE 保留原始写法。哪怕语句带换行、空格、注释,模糊匹配仍有效。
例如,AWR 中某高读 SQL 显示为:
SELECT order_id, total_amt FROM orders WHERE order_date > :b1
可用如下语句反查:
SELECT owner, name, type, line, text
FROM dba_source
WHERE UPPER(text) LIKE '%ORDER_ID%TOTAL_AMT%ORDERS%ORDER_DATE%'
AND type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE BODY')
ORDER BY owner, name, line;
要点:
- 不要用完整语句精确匹配,优先用关键词组合(字段名 + 表名 + 条件关键字)
- 注意大小写:
UPPER(text)统一处理,避免漏掉小写定义的过程 - 若过程含动态 SQL,
text里可能只有EXECUTE IMMEDIATE v_sql,此时要顺藤摸瓜查v_sql赋值位置
真正难的不是找到 SQL,而是确认它是否属于某个 PL/SQL 单元——因为 Oracle 在 v$sql 和 DBA_HIST_SQLSTAT 中彻底剥离了调用栈。你得靠人工交叉验证:SQL 文本特征 + 执行频率模式 + 业务时段 + ASH 中的 PL/SQL 入口 ID。漏掉任意一环,就容易把定时作业里的清洗 SQL,误判成接口层的实时过程。











