PL/SQL execution占比持续高于5%且明显偏离基线即属异常,需结合ASH下钻、匿名块排查、等待事件组合及NATIVE编译模式等综合诊断,而非仅看百分比。

看DB Time分解里PL/SQL execution占比是否异常升高
AWR时间模型(Time Model Statistics)里PL/SQL execution这一项不是“执行PL/SQL花了多少时间”的直白统计,而是Oracle内核在PL/SQL引擎层停留的wall-clock时间总和。它高≠PL/SQL写得差,但**持续高于5%且明显偏离基线**,就该怀疑是瓶颈源头。
常见错误现象:PL/SQL execution从正常0.8%跳到12%,但Top SQL里没出现明显高CPU或高Elapsed的语句;或者DB CPU占比不高,用户却普遍反馈“过程慢”。
实操建议:
- 先确认对比窗口:必须拿问题时段报告和前一天同一时段的正常报告并排看,单看一份容易误判基线
- 查
PL/SQL execution在Time Model Statistics里的绝对值(单位:秒),再除以DB Time算真实占比——别只信百分比栏,有些版本UI会四舍五入掩盖细节 - 如果占比高但
DB CPU占比低,大概率是PL/SQL里大量调用DBMS_LOB、UTL_FILE或嵌套循环+异常捕获(比如WHEN OTHERS THEN NULL),导致控制流在PL/SQL层反复跳转,不进SQL引擎 - 注意排除干扰:
PL/SQL compilation和PL/SQL execution是分开统计的,编译慢(如包体过大、依赖复杂)会抬高前者,不是执行瓶颈
交叉验证SQL ordered by CPU Time里是否存在PL/SQL匿名块
AWR默认把匿名PL/SQL块(比如BEGIN ... END;)归类为SQL_ID为0000000000000000或zzzzzzzzzzzzzzz的伪SQL,不会出现在常规Top SQL列表里。但它的CPU消耗会被计入PL/SQL execution,且常伴随高parse_calls。
实操建议:
- 在SQL Statistics部分,手动搜索
SQL_ID为0000000000000000或空值的行,重点关注CPU Time (s)和Executions列 - 若发现某匿名块
CPU Time (s)高达数百秒,且Executions与应用QPS匹配(比如每秒执行3次×3600秒=1万次),基本锁定为定时任务或应用层批量逻辑 - 用
DBMS_XPLAN.DISPLAY_AWR查不到它的执行计划——它压根没走优化器。要定位具体代码,得结合V$SQLAREA查SQL_TEXT字段(需及时抓取,AWR里不存完整文本) - 检查是否启用了
PLSQL_CODE_TYPE = NATIVE:解释模式(INTERPRETED)下PL/SQL执行效率天然低30%~50%,尤其含大量数学运算时
盯住PL/SQL相关等待事件组合信号
PL/SQL execution本身不产生等待,但它会放大底层资源争用。真正暴露瓶颈的,往往是它触发的配套等待事件组合。
实操建议:
- 重点排查
latch: shared pool和latch: library cache是否同步升高:说明PL/SQL频繁硬解析(比如动态拼SQL未用绑定变量)、或包体太大导致共享池碎片 - 若
enq: TX - row lock contention或enq: TM - contention在Top 5里排前三,且PL/SQL execution占比高,大概率是PL/SQL里用FOR UPDATE NOWAIT重试逻辑不当,或批量更新未分批 - 看到
db file sequential read高 +PL/SQL execution高,别急着加索引——先查PL/SQL里是否有SELECT ... BULK COLLECT INTO加载超大结果集,再做循环处理 - RAC环境下,
gc cr block busy和PL/SQL execution双高,往往指向PL/SQL里跨实例访问全局临时表或序列
用ASH下钻确认PL/SQL调用栈深度
AWR只给聚合值,ASH才能看到PL/SQL哪一层在卡。比如一个三层嵌套存储过程,耗时全在第二层的DBMS_SCHEDULER.CREATE_JOB调用上,但AWR里只显示顶层过程名。
实操建议:
- 运行
SELECT sql_id, program_id, program_line#, event, p1text, p1 FROM v$active_session_history WHERE session_state = 'ON CPU' AND sample_time > SYSDATE-1/24 ORDER BY sample_time DESC - 关注
program_id非零的记录:它对应ALL_OBJECTS.OBJECT_ID,可JOIN查出具体包名和行号 - 若
p1text是file#且p1值很大,说明PL/SQL正在读大文件(UTL_FILE)或LOB段,而非执行逻辑 - 避免只查
SQL_ID:PL/SQL里EXECUTE IMMEDIATE生成的子SQL会有独立SQL_ID,但父过程ID才是根因
PL/SQL瓶颈最难缠的地方在于:它既不像SQL那样有明确执行计划可读,也不像OS级CPU那样能直接杀进程。时间模型只是第一道门,真正要拆开看,必须把AWR、ASH、V$OBJECT_USAGE甚至应用日志串起来——尤其是那些DBMS_OUTPUT.PUT_LINE打满屏幕却没实际业务意义的日志调用,它们在AWR里不显山不露水,但在ASH里会高频占满ON CPU样本。


















