准确定位pl/sql代码块本身等待耗时需查v$active_session_history中plsql_entry_object_id非空且sql_id为null的样本;此时session_state='on cpu'表示纯pl/sql计算开销,'waiting'且event='pl/sql lock timer'则多因dbms_lock.sleep等休眠导致。

直接查 v$active_session_history 并过滤 plsql_entry_object_id 和 sql_id IS NULL,才能准确定位 PL/SQL 代码块本身的等待耗时;只看 SQL_ID 会漏掉纯 PL/SQL 层(如循环、JSON 解析、正则匹配)的 CPU 或等待开销。
怎么确认 PL/SQL 块正在活跃执行中
ASH 不记录 PL/SQL 过程定义,只记录它“正在运行”的那几秒。关键判断依据是:plsql_entry_object_id 非空,且 sql_id 为 NULL —— 这代表当前样本处于纯 PL/SQL 执行层,没发出任何 SQL。
- 先查出目标过程/匿名块的
object_id:SELECT object_id FROM all_objects WHERE owner = 'SCOTT' AND object_name = 'MY_PROC' AND object_type = 'PROCEDURE' - 再查 ASH 中匹配的活跃样本:
SELECT sample_time, session_state, event, p1, p2, p3 FROM v$active_session_history WHERE plsql_entry_object_id = 12345 AND sql_id IS NULL AND sample_time > SYSDATE - INTERVAL '3' MINUTE - 如果
session_state = 'ON CPU'且event为空,说明正忙于 PL/SQL 计算(比如大循环或字符串处理) - 如果
session_state = 'WAITING'且event = 'PL/SQL lock timer',大概率是用了DBMS_LOCK.SLEEP或隐式休眠
为什么查不到 PL/SQL 块的 ASH 样本
不是所有 PL/SQL 执行都会被采样到。常见断点位置包括:
- 过程执行太快(v$active_session_history 没抓到任何样本
- 卡在 PL/SQL 解析阶段(如动态 SQL 的
EXECUTE IMMEDIATE前硬解析),此时plsql_entry_object_id可能未稳定,in_hard_parse = 'Y'更可靠 - 调用的是匿名块(非命名过程),
plsql_entry_object_id为 0,得靠module和action字段辅助识别(如module = 'SQL*Plus'+action = 'ANONYMOUS_BLOCK') - 你误查了
dba_hist_active_sess_history,而问题发生在最近 2 分钟内——历史表默认每 10 秒刷一次,且保留策略常导致刚发生的样本尚未入库
如何区分 PL/SQL 层耗时和它内部 SQL 的耗时
同一个 plsql_entry_object_id 下,样本可能混着两种类型:纯 PL/SQL 耗时(sql_id IS NULL)和它调用的 SQL 耗时(sql_id 非空)。必须分开统计,否则会高估或低估真实瓶颈。
- 查 PL/SQL 自身耗时:
WHERE plsql_entry_object_id = 12345 AND sql_id IS NULL - 查它调用的 SQL 耗时:
WHERE plsql_entry_object_id = 12345 AND sql_id IS NOT NULL - 注意
top_level_sql_id字段:它指向最外层调用者(比如 EXEC 匿名块的 SQL_ID),可帮你确认是否来自应用直连,还是嵌套在另一个存储过程中 - 若发现某
sql_id在多个不同plsql_entry_object_id下高频出现,说明该 SQL 是被多个过程共用的热点语句,优化优先级更高
真正难的是时间对齐:PL/SQL 循环里每轮调用一次 SQL,ASH 可能只采到其中一轮的 SQL 执行,另几轮只采到 ON CPU;不结合 sample_time 精确到秒排序,很容易把“循环慢”误判成“SQL 慢”。











