要查ash报告中cpu占用最高的sql,需筛选session_state='on cpu'的样本并按sql_id统计样本数,1样本≈10ms,数值越大说明cpu消耗越严重;同时结合sql_plan_hash_value、aas、session state等字段综合分析执行计划变化、真实负载及阻塞关系。
怎么看ash报告里cpu占用最高的sql?
ash(active session history)是oracle诊断性能问题最直接的入口,它每秒采样一次活跃会话,天然聚焦在“正在跑”的sql上。关键不是看谁执行次数多,而是看谁在cpu上“霸占时间最长”——session_state = 'on cpu' 的记录才是真凶。
查法很简单:从 v$active_session_history 或 AWR 中提取最近1小时数据,按 sql_id 分组统计 ON CPU 的样本数:
SELECT sql_id, COUNT(*) cpu_samples FROM v$active_session_history WHERE session_state = 'ON CPU' AND sample_time > SYSDATE - 1/24 GROUP BY sql_id ORDER BY cpu_samples DESC FETCH FIRST 5 ROWS ONLY;
-
COUNT(*)就是该 SQL 占用的 CPU 样本数,1个样本 ≈ 10ms,数值越大越可疑 - 别只盯着
sql_id,顺手连上sql_plan_hash_value,同一sql_id可能因绑定变量或统计信息变化跑出不同执行计划,CPU消耗可能差几倍 - 如果返回空,说明高CPU可能来自非SQL活动(比如大量解析、递归调用、PL/SQL循环),得切到
event维度再筛
ASH报告里哪些字段容易被忽略但很关键?
生成的HTML版ASH报告(ashrpti.sql)默认展示前10的SQL,但真正定位根因要盯住三个冷门但致命的字段:
-
Top SQL with Top Events表里的% Activity是相对值,只看它容易误判;必须结合右侧的Avg Active Sessions (AAS)—— 如果某SQL AAS > 1,说明它平均独占了超过1个CPU核心,这是硬指标 -
Wait Class列显示的是等待类型,但ON CPU不属于任何 Wait Class,它单独出现在Session State列;很多人漏掉这列,结果把“等IO”的SQL当成了CPU问题 -
Blocking Session和Blocking Session Status要一起看:如果一个SQL长期ON CPU,但它的Blocking Session非空且状态是VALID,说明它其实在为上游会话“背锅”,真实瓶颈在被阻塞链更前端
为什么同一个sql_id在ASH里CPU占比忽高忽低?
这不是数据不准,而是Oracle执行计划动态变化的真实反映。常见原因有:
- 绑定变量窥探(
bind peeking)导致优化器对不同值生成不同计划,有的走索引快,有的全表扫+CPU密集计算 - 统计信息过期或直方图缺失,让优化器低估/高估返回行数,进而选错连接方式(比如NL vs HASH),后者在内存不足时会大量使用CPU做排序和哈希
- 并行查询(
PX)开启后,sql_id不变,但实际是多个进程同时ON CPU,ASH里会看到多个相同sql_id的样本集中爆发,单次采样看不出,但按分钟聚合就非常明显
验证方法:用 SELECT * FROM v$sql_plan WHERE sql_id = 'xxx' AND plan_hash_value IN (SELECT DISTINCT plan_hash_value FROM v$active_session_history WHERE sql_id = 'xxx'); 对比不同计划的 operation 和 options,重点找 SORT、HASH JOIN、WINDOW SORT 这类CPU大户。
查到高CPU SQL后,下一步不能只看执行计划?
执行计划告诉你“怎么跑”,但不解释“为什么这么跑”。尤其当 cost 很低却CPU飙升时,得挖更深:
- 检查
v$sql视图里的cpu_time和elapsed_time比值:如果cpu_time / elapsed_time > 0.8,基本确认是纯计算瓶颈,不是IO或锁拖慢 - 用
DBMS_XPLAN.DISPLAY_CURSOR(sql_id, child_number, 'ALLSTATS LAST')看实际运行时的STARTS和E-Rows对比,如果STARTS远大于1且A-Rows远小于E-Rows,说明嵌套循环里内表被反复扫描,CPU耗在重复查找上 - 别忘了PL/SQL:如果SQL里调用了自定义函数(
FUNCTION),而函数体包含循环或复杂逻辑,ON CPU样本会记在SQL上,但根因在函数里;这时得开DBMS_PROFILER或 12c+ 的DBMS_MONITOR.SESSION_TRACE_ENABLE才能定位到具体代码行
真实环境里,CPU高的根源常藏在“执行计划看起来没问题”的地方——比如一个 TO_CHAR(date_col, 'YYYYMMDD') 在千万级表上作为过滤条件,优化器选了索引,但函数本身在每行都触发字符串转换,CPU就 quietly 吃掉了。











