查最近5分钟最耗cpu的sql_id,必须用sample_time > sysdate - interval '5' minute动态过滤,rac环境须用gv$active_session_history;count(*)反映on cpu采样次数,1样本≈10ms仅为估算,受_ash_sampling_interval影响,且需结合sql_plan_hash_value分析子游标差异。
查最近5分钟最耗cpu的sql_id,必须加sample_time动态过滤
直接查v$active_session_history里session_state = 'on cpu'样本数最多的sql_id,就是真正在拖垮cpu的那条语句。别用写死的时间字符串(比如'2026-06-13 16:00:00'),系统时区或nls设置一变就查空。
- 正确写法:
SAMPLE_TIME > SYSDATE - INTERVAL '5' MINUTE——精确覆盖最近5分钟,不受会话时区干扰 - RAC环境必须用
gv$active_session_history,否则只看到当前实例的样本,漏掉其他节点上的真实压力点 - 别用
BETWEEN包死区间:采样点是离散的(约每秒1次),问题可能发生在16:02:59.8,但你的区间是16:02:00–16:03:00,而采样只落在.0/.1/.2秒,容易漏掉
为什么COUNT(*)能反映CPU占用强度,但不能直接换算成毫秒
COUNT(*)统计的是该sql_id在ON CPU状态下被采样到的次数,Oracle默认采样频率约1次/秒,所以常有人说“1样本≈10ms”。但这只是粗略估算,实际要小心几个偏差点:
-
_ash_sampling_interval隐含参数可能被调低,高负载时采样变稀疏 - 执行很快的SQL(比如200ms)可能只被采到1–2次,而跑30秒的慢SQL会贡献约30个样本——但后者只执行1次,前者可能执行了100次
- 真正判断“谁更吃CPU”,要看
COUNT(*) / 总ON CPU样本数的占比,而不是绝对数值;避免把高频轻量SQL误判为根因
查到sql_id后,SQL_TEXT为空怎么办
v$active_session_history只存sql_id,不存文本。如果SELECT sql_text FROM v$sql WHERE sql_id = '&sql_id'返回空,大概率是这条SQL已被老化出共享池(比如硬解析风暴后被挤掉)。
- 优先查
DBA_HIST_SQLTEXT:它从AWR快照里捞历史文本,只要该SQL曾进过AWR,就有机会恢复 - 若仍为空,用
sql_plan_hash_value关联DBA_HIST_SQL_PLAN,至少能还原执行计划结构 - 注意
sql_id相同但child_number不同,意味着子游标不同——同一sql_id下可能有完全不同的执行路径和性能表现,别忽略这个字段
容易被当成CPU问题,其实根本不在SQL层
如果按session_state = 'ON CPU'查出来一堆记录,但sql_id全为空或全是'0000000000000000',说明问题不在业务SQL,而在Oracle自身动作:
-
program含oracle@ (J00):很可能是job queue slave在跑没限制的PL/SQL循环(如FOR i IN 1..1000000 LOOP ... END LOOP;) -
session_type = 'BACKGROUND'且event频繁出现latch: cache buffers chains:可能是DBWR写压力过大或热块争用,不是SQL问题 -
event是cursor: pin S wait on X或library cache lock:大概率是应用反复建连接+硬解析,得看连接池配置和绑定变量使用情况
ASH数据只在内存保留约1小时,查之前务必确认时间窗口是否还在有效期内;一旦滚动丢弃,就得切到DBA_HIST_ACTIVE_SESS_HISTORY查历史,但要注意plan_hash_value和当时执行计划的匹配性。











