很多用户查询AWR报告时发现SQL执行次数对不上,第一反应是怀疑数据存在错误。实际上数据本身没有错,而是查询位置不对、时间粒度选错,或者忽略了快照本身固有的漏采问题。

为什么V$SQL.EXECUTIONS不能作为可靠依据
V$SQL中的EXECUTIONS字段是内存中游标生命周期内的累计值,并非历史事实依据。一旦游标因老化(age out)被淘汰,计数就会立即清零,实例重启时整条记录也可能直接消失。它只反映“当前仍在缓存中的这条SQL被执行的次数”,并不等同于“过去一小时内真实发生的执行次数”。
举一个典型场景:在V$SQL中看到EXECUTIONS = 7,但同一SQL在DBA_HIST_SQLSTAT里SUM(EXECUTIONS_DELTA)却是1280——这说明该SQL刚经历过硬解析,旧的子游标已被淘汰,计数出现了断层。因此,仅依赖V$SQL判断执行频率,很容易得出错误结论。
DBA_HIST_SQLSTAT.EXECUTIONS_DELTA才是可追溯的正确指标
该字段来源于AWR快照,默认每小时采样一次,记录的是两次快照间的增量值。它不可篡改且携带时间戳,支持关联验证。不过使用时需要注意以下几点:
- 必须与
DBA_HIST_SNAPSHOT通过SNAP_ID和INSTANCE_NUMBER双条件进行JOIN,否则在RAC环境下会因跨实例导致不匹配; BEGIN_INTERVAL_TIME是TIMESTAMP类型,如果直接使用TRUNC(BEGIN_INTERVAL_TIME)会坍缩为天粒度,正确的写法是TO_CHAR(N.BEGIN_INTERVAL_TIME, 'YYYY-MM-DD HH24');- 务必注意时区以数据库服务器为准,而非客户端时区。
AWR快照本身会遗漏高频短周期执行
默认每3600秒采样一次,如果某条SQL在2分钟内集中执行了500次,而快照恰好落在该SQL执行前1秒和执行后1秒进行采集,那么EXECUTIONS_DELTA就会显示为0——并不是没有执行,而是采样窗口未能覆盖。这种漏采现象在API洪峰、批处理触发等场景中非常常见。验证方法很简单:查询V$SQL.LAST_ACTIVE_TIME和EXECUTIONS,对比最近几分钟内是否仍然活跃。若要真正捕捉这类瞬时行为,必须依赖V$ACTIVE_SESSION_HISTORY(ASH),它每秒采样一次。
topnsql参数限制导致SQL根本未进入快照
AWR不会无差别收录所有SQL。topnsql参数控制每个快照周期内按elapsed_time排名前N的SQL入库。即使设置为100,如果该周期内其他SQL更耗时,你的轻量OLTP语句仍然进不去。需要留意的是:修改这个参数只对新快照生效,旧快照不会回填。确认SQL是否被采样,不要只盯着AWR HTML报告的“Top SQL”页面,直接查询DBA_HIST_SQLSTAT中是否存在对应sql_id,这才是确凿的证据。
归根结底,执行次数统计的误差往往不是数值计算错误,而是你默认“AWR应当包含所有信息”,却没有意识到它本质上是稀疏采样系统——高频、短时、低耗的SQL本身就处于AWR的探测盲区。只有清楚这些边界条件,才不会被数据表象所迷惑。
