AWR报告抓不住存储过程解析问题,因动态SQL被归入父PL/SQL单元、快照粒度粗(1小时)、采不到短时硬解析峰值;需结合ASH的in_hard_parse列、plsql_entry及SQL文本定位具体语句。

为什么单独看AWR报告抓不住存储过程的解析问题
AWR报告里SQL ordered by Parse Calls模块确实会列出解析次数高的SQL,但存储过程里的动态SQL(比如EXECUTE IMMEDIATE拼接语句)通常不会以独立SQL_ID形式出现在这里——它被归入父PL/SQL单元,只显示为anonymous block或过程名。你看到的是“某过程调用频繁”,却不知道里面哪一行SQL在疯狂硬解析。
更麻烦的是,AWR快照默认每小时一次,而一个批处理作业引发的硬解析风暴可能只持续3分钟。快照采不到峰值,Top 5 Timed Events里只看到模糊的library cache: mutex X或cursor: pin S wait on X,根本看不出源头。
- 确认是否真有硬解析:查
v$sysstat中parse count (hard)增量,对比正常时段每秒增幅是否超50次 - 别只盯
DBA_HIST_SQLSTAT:它的SQL文本可能被截断,且不包含in_hard_parse上下文 - 注意
SYSAUX表空间压力:ASH数据存在这里,若空间不足或争用高,v$active_session_history采样会丢帧
用ASH定位存储过程中具体哪条动态SQL在硬解析
关键不是查“谁在解析”,而是查“谁在解析时卡住了”。v$active_session_history的in_hard_parse列+sql_id组合,能直接锁定问题语句。
执行这个查询(时间窗口设为问题发生后10分钟内):
SELECT sql_id, sql_text, COUNT(*) cnt FROM v$active_session_history a JOIN v$sql b ON a.sql_id = b.sql_id WHERE a.in_hard_parse = 'Y' AND a.sample_time > SYSDATE - INTERVAL '10' MINUTE AND b.sql_text LIKE '%yuan_obj%' -- 替换为你的表名或关键字 GROUP BY sql_id, sql_text ORDER BY cnt DESC;
如果返回空,说明硬解析发生在PL/SQL内部未缓存的匿名块里,此时要加session_id和plsql_entry字段回溯:
-
plsql_entry显示当前正在执行的过程名,比如PKG_BATCH.PROC_LOAD_DATA -
current_obj#可关联dba_objects查出是哪个包体或过程 - 若
sql_id为空但in_hard_parse='Y',基本确定是未使用绑定变量的字符串拼接
用AWR快照验证ASH发现的根因是否全局生效
ASH告诉你“某条SQL在硬解析”,AWR要回答“这个行为是否拖垮了整个实例”。重点比对两个快照区间:
- 问题时段快照(比如
SNAP_ID12345→12346)vs 正常时段快照(如12300→12301) - 在
Instance Efficiency Percentages里看Parse CPU to Parse Elapsed %是否从95%暴跌到 - 在
Shared Pool Statistics里检查Hard Parse Ratio(硬解析占比),超过10%就危险 - 对比
Library Cache Activity中pins和reloads的比率,reload/pin > 0.01说明共享池碎片严重
特别注意:如果ASH发现硬解析集中在某个过程,但AWR里Library Cache Hit %仍高于99%,说明问题尚未扩散——这是修复的黄金窗口期,还没影响其他会话。
生成带PL/SQL上下文的ASH报告避免误判
默认@?/rdbms/admin/ashrpt脚本生成的HTML报告会折叠PL/SQL调用栈,你只能看到anonymous block,看不到是哪个过程调用的。必须手动指定report_level参数:
SQL> @?/rdbms/admin/ashrpt Enter value for report_type: html Enter value for begin_time: 2026-10-26/15:30:00 Enter value for duration: 30 Enter value for report_level: 'typical' -- 改成 'all' 才显示完整调用栈
report_level='all'会让报告中每个ASH采样点都展开plsql_entry、plsql_subprogram_id、甚至sql_plan_hash_value,你能清晰看到“PKG_ETL.LOAD_FACT_SALES第87行调用EXECUTE IMMEDIATE 'INSERT ...'导致硬解析”。
容易忽略的细节:
- Oracle 12c+才支持
report_level='all',11g只能靠v$active_session_history手工关联 - 若过程用了
PRAGMA AUTONOMOUS_TRANSACTION,其ASH采样会独立于主事务,需单独查session_id过滤 -
in_parse='Y'但in_hard_parse='N'的情况,往往是软解析等待library cache lock,这时要查阻塞者而非SQL本身
真正难的不是查到哪行代码有问题,而是确认这个存储过程是否被多个应用并发调用——同一段低效代码,在单用户下只是慢,在200并发下就是HANG。这需要把ASH的session_id和应用日志里的请求ID做时间戳对齐,而不是只盯着数据库视图。


















