ASH可直接定位CPU热点,但v$active_session_history仅保留1小时数据且采样非连续,查最近5分钟需用SAMPLE_TIME > SYSDATE - INTERVAL '5' MINUTE;RAC环境须用gv$;ON CPU样本按SQL_ID聚合计数可快速识别高耗SQL;SQL文本可能因老化缺失,需结合V$SQL或DBA_HIST_SQLTEXT查询;latch: cache buffers chains等待未必缺索引,需分析热块对象及B-Tree结构;ASH报告无SQL文本因仅存SQL_ID,依赖共享池或AWR历史视图;根本原因需交叉验证参数、统计信息、执行计划及存储性能。
能直接定位到哪几秒、哪个会话、哪条sql在疯狂占cpu,ash 就够了;别等 awr 出报告,它慢半拍,可能已经错过抖动窗口。
查 v$active_session_history 时为什么总看不到刚发生的 CPU 高峰?
因为 v$active_session_history 默认只保留最近 1 小时数据,且采样是概率性(每秒约 1 次),不是全量连续捕获。你看到“查不到”,大概率是时间范围没卡准,或者已经滚动丢弃。
- 务必用
SAMPLE_TIME > SYSDATE - INTERVAL '5' MINUTE这类动态条件,别写死时间字符串——系统时区、NLS 设置稍有偏差就查空 - 单实例查
v$active_session_history即可;RAC 环境必须用gv$active_session_history,否则只看到本节点 - 如果问题发生在 13:22:18,但你查
SAMPLE_TIME BETWEEN '13:22:00' AND '13:22:30',仍可能漏掉——采样点未必刚好落在这个区间内,建议放宽到 ±15 秒
如何快速揪出最近 5 分钟最耗 CPU 的 SQL?
核心逻辑:只统计 SESSION_STATE = 'ON CPU' 的样本,按 SQL_ID 聚合计数,数量越多,说明该 SQL 在 CPU 上执行越久。
- 执行以下语句(注意替换为你的实际环境):
SELECT ASH.SQL_ID, (SELECT SQL_TEXT FROM V$SQLAREA WHERE SQL_ID = ASH.SQL_ID AND ROWNUM = 1) SQL_TEXT, COUNT(*) SAMPLES FROM V$ACTIVE_SESSION_HISTORY ASH WHERE SESSION_STATE = 'ON CPU' AND SAMPLE_TIME > SYSDATE - INTERVAL '5' MINUTE GROUP BY ASH.SQL_ID ORDER BY SAMPLES DESC FETCH FIRST 5 ROWS ONLY; - 如果
V$SQLAREA查不到文本,说明该 SQL 已被老化出共享池,此时要结合SQL_PLAN_HASH_VALUE和历史执行计划分析 - 别忽略
SQL_CHILD_NUMBER——同一SQL_ID下不同子游标可能因绑定变量窥探导致执行计划天差地别
latch: cache buffers chains 频繁出现,是不是一定得加索引?
不一定。这个等待事件本质是多个会话争抢同一个 buffer header 的 latch,常见于热块(hot block)竞争,而热块不等于“没索引”,更可能是“索引根块/分支块被反复访问”或“小表全扫+无谓排序”。
- 先确认
CURRENT_OBJ#是否集中于某张表或索引:查v$active_session_history中该事件对应行的CURRENT_OBJ#,再关联dba_objects看对象名 - 如果是索引对象,检查其 B-Tree 高度是否异常(>4),或叶块是否极度倾斜(
INDEX_STATS中的BLEVEL和LF_ROWS_LEN / LF_BLKS比值) - 如果
PROGRAM列大量是oracle@... (J000),可能是物化视图快速刷新任务在刷同一个基表,需调优刷新策略
为什么用 DBMS_WORKLOAD_REPOSITORY.ASH_REPORT_TEXT 生成的报告里看不到完整 SQL 文本?
因为 ASH 报告默认只存 SQL_ID,不存原始 SQL 文本;文本从 V$SQL 或 DBA_HIST_SQLTEXT 中反查,而这两张视图里的记录生命周期远短于 ASH 数据本身。
- 运行报告前,先确保目标 SQL 还在共享池里:
SELECT SQL_TEXT FROM V$SQL WHERE SQL_ID = 'xxx' - 若已老化,可尝试从 AWR 快照中捞:
SELECT SQL_TEXT FROM DBA_HIST_SQLTEXT WHERE SQL_ID = 'xxx' AND DBID = (SELECT DBID FROM V$DATABASE) - 更稳妥的做法:在问题复现前,提前运行
ALTER SYSTEM FLUSH SHARED_POOL是下策;上策是用DBMS_SQLTUNE.CREATE_SQLSET把可疑 SQL 捕获进 SQL Tuning Set,避免文本丢失
真正难的不是查哪条 SQL 耗 CPU,而是判断它为什么在这个时刻突然变重——参数变更、统计信息过期、执行计划突变、底层存储延迟升高,这些都得交叉验证。ASH 给你起点,不是终点。











