查最近5分钟最耗cpu的sql_id,须用sample_time > sysdate - interval '5' minute动态过滤,避免时区与采样非连续性问题;rac环境需用gv$active_session_history;count(*)反映on cpu采样次数,约等于10ms/cpu样本,但受采样频率影响。
直接查 v$active_session_history,用 session_state = 'on cpu' + 动态时间窗口,5分钟内就能定位到哪条 sql_id 在哪几秒吃掉了最多 cpu——ash 本身不“分析”,它只记录;分析动作全靠你写的查询条件是否精准。
查最近5分钟最耗CPU的SQL_ID,必须加SAMPLE_TIME动态过滤
很多人查空,不是没数据,是时间条件写死了。系统时区、NLS_DATE_FORMAT、采样非连续性,都会让 '2026-04-30 12:00:00' 这种字符串失效。
正确做法是用间隔运算:
-
SAMPLE_TIME > SYSDATE - INTERVAL '5' MINUTE—— 精确覆盖最近5分钟,不受会话时区影响 - 别用
BETWEEN包死区间,采样点可能落在边界外(比如问题发生在 12:02:59.8,而你查的是 12:02:00–12:03:00,但采样只在 .0、.1、.2… 秒发生) - RAC 环境必须用
gv$active_session_history,否则只看到当前实例的样本
为什么COUNT(*)就是CPU消耗量级?1样本≈10ms这个换算要谨慎用
COUNT(*) 是该 SQL_ID 在 ON CPU 状态下被采样到的次数,Oracle 默认每秒采样约 1 次,所以理论上有「1 样本 ≈ 10ms CPU 时间」的说法。但这只是估算值。
- 实际采样频率受
_ash_sampling_interval隐含参数影响,某些高负载实例可能降频 - 如果 SQL 执行很快(ON CPU
- 真正要对比消耗程度,应看
COUNT(*) / 总样本数的占比,而不是绝对数值——避免把执行频次高但单次很轻的 SQL 误判为根因
查到SQL_ID后,SQL_TEXT为空怎么办?别硬刷v$SQL
v$active_session_history 只存 SQL_ID,不存文本。查 v$sql 返回空,大概率是这条 SQL 已老化出共享池(比如硬解析风暴后被挤掉)。
- 优先查
DBA_HIST_SQLTEXT:它从 AWR 快照里捞历史文本,只要该 SQL 曾进过 AWR,就有机会恢复 - 若仍为空,用
SQL_PLAN_HASH_VALUE+PLAN_HASH_VALUE关联DBA_HIST_SQL_PLAN,至少能还原执行计划结构 - 注意
sql_id相同但child_number不同的子游标,可能对应完全不同的计划和性能表现,别忽略CHILD_NUMBER字段
瞬时飙升常伴随“假性ON CPU”,怎么区分真忙和假等?
有些会话显示 SESSION_STATE = 'ON CPU',但实际在等资源——比如等 latch: cache buffers chains 或陷入 runqueue 排队,OS 层已无空闲 CPU 调度它。
- 结合
EVENT列一起看:如果SESSION_STATE = 'ON CPU'但EVENT是NULL或read by other session,更可能是真实计算密集型 - 如果
EVENT是latch: row cache objects或enq: SQ - contention,说明它正在争抢内部资源,CPU 占用是争用副产品,不是 SQL 逻辑本身导致 - 此时查
v$session的STATE和EVENT,再比对gv$system_event中同实例的等待事件总量,能快速判断是全局争用还是局部热点
真实问题往往藏在「ON CPU 样本多但 SQL_TEXT 找不到」「同一 SQL_ID 的 AAS 在不同时间段差几倍」「RAC 中某节点样本激增但其他节点平稳」这些细节里。别只盯着 top 1 的 SQL_ID,要顺着 INST_ID、CHILD_NUMBER、PLAN_HASH_VALUE 和 EVENT 多维交叉验证——ASH 给的是线索,不是结论。











