dbms_profiler仅标识高耗时pl/sql行号,真瓶颈多在调用的sql中;应优先查v$session中active且seconds_in_wait长的会话,再用dbms_xplan.display_cursor分析其sql_id的实际执行计划,重点关注rows与e-rows偏差、等待事件及绑定变量类型匹配。

DBMS_PROFILER 只能告诉你哪行 PL/SQL 耗时高,但真瓶颈八成藏在它调用的 SQL 里;直接查 v$session + DBMS_XPLAN.DISPLAY_CURSOR 比等 profiler 出报告快得多。
怎么抓正在跑慢的存储过程“活口”?
别等它执行完——立刻查当前活跃会话,重点筛出卡住的那几个:
SELECT sid, serial#, sql_id, event, seconds_in_wait FROM v$session WHERE status = 'ACTIVE' AND username IS NOT NULL ORDER BY seconds_in_wait DESC
常见等待事件含义:
-
db file sequential read:索引走得多、单块读频繁,可能没走对索引或数据分布倾斜 -
cursor: pin S wait on X:硬解析太多,可能是绑定变量类型不匹配或 SQL 文本拼接 -
enq: TX - row lock contention:存储过程里有未提交的 UPDATE/INSERT,锁住了别人
sql_id 后马上看实际执行计划:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('your_sql_id', NULL, 'ALLSTATS LAST'))
重点关注:
-
Rows(实际)和E-Rows(预估)是否差 10 倍以上——说明统计信息过期或谓词写法触发了错误估算 - 有没有
TABLE ACCESS FULL出现在大表上 - 有没有
NESTED LOOPS驱动了上万行去 probe 小表
为什么 DBMS_PROFILER 查不到数据?
不是工具失效,是三个硬性条件漏掉一个就白忙:
- 用户必须显式被授予
EXECUTE ON DBMS_PROFILER——角色权限(比如EXECUTE ANY PROCEDURE)不生效 - 目标存储过程得用
ALTER PROCEDURE your_proc COMPILE DEBUG重新编译,否则行号对不上 - 必须在同一会话内走完完整闭环:
START_PROFILER→ 执行过程 →FLUSH_DATA→STOP_PROFILER;漏掉FLUSH_DATA,数据就卡在 PGA 里出不来
plsql_profiler_runs、plsql_profiler_units、plsql_profiler_data运行
@?/rdbms/admin/proftab.sql 创建它们,并给用户授 SELECT/INSERT/UPDATE/DELETE 权限。
BULK COLLECT + FORALL 为什么还是慢?
用了 BULK COLLECT 不等于自动变快,关键在后续处理逻辑:
- 常见错误:FETCH 到集合后,还用
FOR i IN 1..coll.COUNT LOOP UPDATE ... WHERE id = coll(i).id——本质仍是 N 次单行 DML - 正确写法:把 DML 移到循环外,用
FORALL i IN 1..coll.COUNT UPDATE t SET x = coll(i).x WHERE id = coll(i).id -
LIMIT值设太小(如 100)导致分批太碎,CPU 花在反复调度上;设太大(如 100000)可能触发ORA-04030(PGA 不足);建议从 5000 起调,同时监控v$pgastat的total PGA allocated
BULK COLLECT 也救不了——得先把这些依赖变成批量操作。绑定变量导致执行计划漂移,怎么验证?
PL/SQL 里写 WHERE order_id = :p_id 看似安全,但一旦 p_id 是 VARCHAR2 类型而表字段是 NUMBER,就会隐式转换、索引失效:
- 验证方法:把该 SQL 单独拿出来,把
:p_id替换成字面量(如12345),再跑EXPLAIN PLAN;如果这时走了索引,问题就出在绑定类型 - 临时解法加提示:
/*+ INDEX(t idx_order_date) */,但只是绕过问题 - 长期方案:统一变量类型(比如声明
p_id NUMBER),或显式转换:WHERE order_id = TO_NUMBER(:p_id)
真正卡住的地方,往往不在 PL/SQL 行号本身,而在它背后那条 SQL 的执行路径、内存分配方式或 I/O 分布。盯着 DBMS_PROFILER 报告里的 TOTAL_TIME 最大值,不如先看 v$session 里谁在等、等什么、等多久。











