AWR报告是定位高资源消耗SQL的起点,通过快照聚合历史执行统计,揭示总耗时高、占比大或单次耗时超1秒的SQL,而非简单查“慢SQL”。
awr 报告不是“查慢 sql”的开关,而是定位高资源消耗 sql 的证据链起点——它不直接标出“执行超 1 秒”的语句,而是告诉你哪些 sql 吃掉了最多 db time。
为什么 V$SQL 查不到的“慢 SQL”在 AWR 里反而明显
因为 V$SQL 只保留共享池中当前缓存的 SQL,一旦被老化淘汰或硬解析后未重用,就消失了;而 AWR 是定时采样(默认每小时 1 次快照),把历史执行统计持久化进 DBA_HIST_SQLSTAT。所以:
- 高频低耗 SQL(比如每秒执行 20 次、单次 50ms)在
V$SQL里可能排不到前几,但在 AWR 的SQL ordered by Elapsed Time里总耗时会冲到 Top 3 - 夜间批处理跑完就退出的会话,其 SQL 在
V$SQL中早已清空,但 AWR 快照里仍留有完整执行次数、CPU/IO 消耗记录 -
V$SQL的ELAPSED_TIME是累计值,但没归一化到时间窗口;AWR 报告自动按快照周期聚合,能算出“该 SQL 占本时段 DB Time 的 12.7%”这种可比指标
生成 AWR 报告时最容易填错的两个 snap_id
执行 @$ORACLE_HOME/rdbms/admin/awrrpt.sql 后,系统会让你输入 begin_snap 和 end_snap。常见错误是:
- 直接填“昨天 10 点”对应的时间戳——不行,必须先查快照 ID:
SELECT snap_id, begin_interval_time FROM dba_hist_snapshot WHERE TRUNC(begin_interval_time) = TRUNC(SYSDATE-1) ORDER BY snap_id; - 选的快照范围太窄(比如只隔 2 个 snap),导致样本太少,
Top 5 Timed Events里全是DB CPU,看不出 IO 压力分布;建议至少覆盖 30 分钟以上,且避开数据库刚启动后的冷启动抖动期 - 误把
end_snap设成比begin_snap小的值,脚本静默失败,生成空报告——检查输出末尾是否有Report written to行
看懂 “SQL ordered by Elapsed Time” 表头的真实含义
这个表格常被误读为“最慢的单条 SQL”,实际它是按“总耗时 = 单次耗时 × 执行次数”排序。关键列解释:
-
Elapsed Time(s):不是单次执行时间,是该 SQL 在整个快照周期内所有执行的耗时总和(单位秒) -
Executions:执行次数。如果这列是 1,但Elapsed Time(s)高达 300,说明真有单次慢查询;如果是 12000,那平均每次才 0.025 秒,问题在频率而非单次效率 -
Elap per Exec(s):平均每次执行耗时。真正要盯紧的是这个值 > 1 秒,且Executions不为 0 的组合 -
%Total DB Time:占比超过 5% 的 SQL 值得优先介入,哪怕执行次数不多
示例:SQL ID: 7z9v8x4m2n1k3 行显示 Elapsed Time(s): 4280、Executions: 8、Elap per Exec(s): 535 —— 这就是典型的单次执行卡顿,下一步必须用 DBMS_XPLAN.DISPLAY_AWR 看执行计划。
拿到 SQL ID 后,别急着优化,先确认是否还在运行
AWR 里的 SQL ID 可能已不在内存中,直接查 V$SQL_PLAN 会返回空。稳妥做法是:
- 先查它最近是否活跃:
SELECT sql_id, child_number, plan_hash_value, last_active_time FROM v$sql WHERE sql_id = '7z9v8x4m2n1k3' AND last_active_time > SYSDATE - 1/24;(1 小时内活跃过) - 如果没结果,再从 AWR 拉历史计划:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('7z9v8x4m2n1k3'));注意:不指定plan_hash_value会返回该 SQL 的所有历史计划,需人工比对 timestamp - 特别警惕
Plan Hash Value变动频繁的 SQL——说明执行计划不稳定,可能是绑定变量窥探、直方图缺失或统计信息过期导致,此时加索引可能无效,得先 fix 统计信息或改写 SQL
真实场景中,一半以上的“AWR Top SQL”问题根源不在 SQL 本身,而在执行计划漂移或业务调用模式不合理。盯着 SQL Text 改写,不如先看 SQL Module 列确认是哪个应用模块发起的,再找开发一起看调用频次和参数分布。


















