真瓶颈在Elapsed Time per Exec (s) > 1的SQL,而非总耗时最长者;需结合Executions交叉验证:高调用+高单次耗时才属高风险。

别直接信“耗时最长”的SQL——AWR里SQL ordered by Elapsed Time显示的是总耗时,不是单次慢,真瓶颈往往藏在Elapsed Time per Exec (s) > 1 的语句里。
看懂SQL ordered by Elapsed Time表头的真实含义
这个表格默认按Elapsed Time(所有执行的累计耗时)降序排列,但它的数值本身不说明“这条SQL慢”,只说明“它被调得勤或单次真长”。关键字段要交叉验证:
-
Executions:如果值很大(比如 >500),再看Elapsed Time per Exec (s)——高调用+低单次耗时 = 合理;高调用+高单次耗时 = 高风险 -
Elapsed Time per Exec (s):真正该盯住的指标。> 1 秒才值得优先分析; -
CPU Time (s)vsElapsed Time (s):若后者是前者的 2 倍以上,说明大量时间花在等待(I/O、锁、latch),不是SQL写得差,而是资源争用
拿到SQL ID后怎么查完整语句和执行计划
SQL ID是唯一入口,但AWR报告里只显示前 60 字符(常被截断),必须查数据字典补全:
- 查完整文本:
SELECT sql_text FROM dba_hist_sqltext WHERE sql_id = 'your_sql_id' AND dbid = &dbid;(注意dba_hist_sqltext可能没存历史语句,优先试v$sql) - 查指定快照区间的执行计划:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('your_sql_id', NULL, &snap_id_begin, &snap_id_end)); - 查最近一次执行的计划(更准):
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('your_sql_id', NULL, 'ALLSTATS LAST'));(需statistics_level=ALL)
为什么Elapsed Time per Exec高,但v$sql里elapsed_time/ executions对不上
AWR 和 v$sql 统计口径不同,常见原因有:
-
v$sql是内存中当前游标缓存的聚合值,会受cursor_sharing、绑定变量窥探、游标老化影响,可能丢失部分执行记录 - AWR 的
dba_hist_sqlstat是每小时采样一次的快照,取的是两个快照之间的差值,中间若发生游标刷新,差值就失真 - 最稳妥做法:用
dba_hist_sqlstat查对应快照区间内的elapsed_time_delta / executions_delta,别直接除v$sql里的总量
容易被忽略的陷阱:快照时间窗口选错
生成AWR报告时选的begin_snap_id和end_snap_id必须覆盖问题时段的**完整业务周期**:
- 报表在 9:15–9:45 跑得慢?别选 9:00–10:00 的快照——高峰期被平均掉了
- 快照间隔默认 60 分钟,但如果你的问题只持续 10 分钟,就得靠
ASH(v$active_session_history)定位,AWR 粗粒度下根本看不到 - 确认快照时间:
SELECT snap_id, begin_interval_time, end_interval_time FROM dba_hist_snapshot ORDER BY snap_id DESC;,别光看ID数字大小
真正卡住系统的,从来不是“总耗时最长”的那条SQL,而是单次执行超过1秒、又高频调用、且CPU Time占比异常低的语句——它大概率正在等I/O或锁,而不是在跑逻辑。


















