AWR是验证执行计划是否真实漂移的唯一依据,需查DBA_HIST_SQLSTAT确认plan_hash_value变化,并用DBMS_XPLAN.DISPLAY_AWR对比差异,结合disk_reads_delta、elapsed_time_delta等指标综合判断性能下降根因。

查 DBA_HIST_SQLSTAT 确认 plan_hash_value 是否真变了
执行计划“变慢”不等于“变了”,AWR 是唯一能验证真实执行路径是否漂移的依据。只看 V$SQL 或当前执行计划,容易被缓存干扰或单次采样误导。
运行这个查询(替换 your_sql_id):
SELECT plan_hash_value, COUNT(*), MIN(sample_time), MAX(sample_time) FROM dba_hist_sql_plan p JOIN dba_hist_sqlstat s USING (sql_id, plan_hash_value) WHERE sql_id = 'your_sql_id' AND sample_time > SYSDATE - 7 GROUP BY plan_hash_value ORDER BY MIN(sample_time);
- 返回多行 → 计划确实在过去 7 天内漂移过,继续往下查
- 只有一行 → 性能下降大概率与执行路径无关,应转向 I/O、锁、内存或网络排查
-
dba_hist_sql_plan默认每sql_id最多存 1000 行计划,高频 SQL 可能被截断;空结果 ≠ 没历史计划
用 DBMS_XPLAN.DISPLAY_AWR 对比两个 plan_hash_value 的实际差异
光知道 plan_hash_value 变了没用,得确认变化点是否致命:是索引扫描退化成全表扫描?还是嵌套循环被替换成 HASH JOIN 导致大量 TEMP 写入?
必须用 ADVANCED 格式才能看到谓词推入、绑定值窥探结果和基数预估偏差:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR( sql_id => 'your_sql_id', plan_hash_value => 1234567890, format => 'BASIC +PEEKED_BINDS +NOTE' ));
- 重点比对
access和filter谓词是否还落在索引列上 - 检查
cardinality预估是否比实际行数大 10 倍以上(比如预估 100,实际返回 12 万) - 若输出中无
PEEKED_BINDS,看不到真实绑定值,可能误判谓词有效性 - 提示 “no rows selected” 不代表计划不存在,常见于并行计划或递归调用未完整记录
交叉验证 disk_reads_delta 和 elapsed_time_delta 是否同步突增
执行计划突变本身不写磁盘,但会引发连锁 I/O 行为——比如全表扫描触发大量 db file sequential read,或 HASH JOIN spill 到 TEMP 表空间导致 physical writes 暴涨。
- 查
DBA_HIST_SQLSTAT:定位disk_reads_delta突增时段(如某快照从 500 跳到 12 万),记下sql_id和snap_id - 查同一
snap_id范围内该sql_id是否出现新plan_hash_value,且旧计划消失 - 若
elapsed_time_delta没明显涨,但disk_reads_delta翻倍 → 基本可断定是 I/O 路径恶化,而非 SQL 逻辑变重 - RAC 环境下必须分
inst_id查,否则聚合后的physical_writes会掩盖单节点热点
排除统计信息更新、自动任务等干扰源
同一 sql_id 在非业务时段集中爆发多个 plan_hash_value,大概率不是应用行为,而是后台任务驱动。
- 过滤掉
sql_text含DBMS_STATS、DBMS_MVIEW、RMAN的语句——它们是任务本身,不是受害者 - 查
DBA_SCHEDULER_JOB_RUN_DETAILS,对齐任务启动时间与 AWR 快照end_interval_time - 比对 Load Profile 中
Redo Generated/sec和Logical reads/sec是否阶梯式跳升(如前一快照 8.5k,下一快照 24.3k) - 若
Physical reads direct占比超 30%,优先怀疑大表分析或备份操作,而非 SQL 自身问题
真正难的不是找到 plan_hash_value 变了,而是确认那个“旧计划”在当时是否真的快——它可能只是没被充分执行、没触发 spill、或者碰巧数据块都在 buffer cache 里。AWR 里的 buffer_gets_delta 和 disk_reads_delta 必须和 executions_delta 一起看,单看平均值会漏掉毛刺。











