如何用Oracle ASH分析过去一小时CPU最高SQL
通过v$active_session_history筛选过去一小时ONCPU会话,按SQL_ID分组计数降序,关联dba_hist_sqltext获取文本,快速定位CPU消耗最高的SQL。注意时区偏差及ASH与AWR统计差异。
在 Oracle 运维中,系统突然变慢、CPU 飙升至 100% 是极为常见的紧急场景。快速定位“过去一小时里,究竟是哪条 SQL 语句吃掉了大量 CPU”是 DBA 的核心需求。如果此时慢吞吞地跑 AWR 报告,等报告生成完毕,热点可能早已过去。相比之下,ASH(Active Session History)才是真正的“快速响应部队”,能够在几秒内锁定高 CPU 消耗的 SQL。
如何通过 ASH 视图查询过去一小时内 CPU 消耗最高的 SQL?
核心查询方法非常简单:利用 v$active_session_history 视图。该视图每秒对活跃会话采样一次,其中 session_state 字段会明确标识会话状态。当值为 'ON CPU' 时,表示该会话在采样瞬间正在消耗 CPU。因此,判断一条 SQL 是否为“CPU 大户”,并不取决于它的总执行时长,而是看它在 CPU 上被“捕获”的次数——采样次数越多,CPU 占用强度自然越大。

在实际操作中,有几个细节容易出错,需要特别留意:
- 时间范围应使用
sample_time >= SYSDATE - 1/24,而不是SYSDATE - INTERVAL '60' MINUTE。后者在部分 Oracle 版本中可能因时区或 NLS 设置导致采样范围偏差,影响数据准确性。 - 聚合分析时,
sql_id是最稳定的键。它比对sql_text直接聚合更可靠,因为同一逻辑的 SQL 可能因绑定变量值不同或硬解析导致文本不完全一致。 - 最关键的一步:
WHERE session_state = 'ON CPU'条件绝对不能遗漏。如果不加此条件,会将等待事件(如 I/O 等待、锁等待)的会话一并纳入统计,结果会完全失真。
如何按 CPU 采样次数排序并关联 SQL 文本?
获取采样次数后,下一步自然是查看这些 SQL 的具体内容。直接关联 v$sql 虽然速度较快,但有一个常见陷阱:当 shared pool 压力过大时,SQL 游标可能已被 aged out。因此,更稳妥的做法是关联 dba_hist_sqltext,该视图保存了历史 SQL 文本,只要有 AWR 权限即可查询。
一个典型的查询语句如下:
SELECT s.sql_id,
COUNT(*) AS cpu_samples,
t.sql_text
FROM v$active_session_history s
JOIN dba_hist_sqltext t ON s.sql_id = t.sql_id
WHERE s.session_state = 'ON CPU'
AND s.sample_time >= SYSDATE - 1/24
AND t.sql_text IS NOT NULL
GROUP BY s.sql_id, t.sql_text
ORDER BY cpu_samples DESC
FETCH FIRST 5 ROWS ONLY;
使用此查询时需要注意几个容易中招的地方:
- 如果发现
sql_text显示为/* SQL Analyze(123) */ SELECT ...等自动任务 SQL,不要急于将其视为问题。需结合program或module字段确认是否来自 DBMS_SQLTUNE 等维护操作——这类 SQL 通常并非真正的业务瓶颈。 - 对于绑定变量极多、SQL 超长的场景,
dba_hist_sqltext默认只存储前 1000 个字符。如需查看完整内容,可使用sql_fulltext字段(Oracle 11g 及之后版本支持)。
为什么查到的 TOP SQL 与 AWR 报告不一致?
这是经典困惑,许多人会遇到。其实两者的统计逻辑完全不同:ASH 是实时的瞬间快照,关注“谁在某一刻吃 CPU”;AWR 是汇总统计,关注“谁在整个快照周期内平均负载更高”。
举例说明:一条 SQL 仅运行 5 秒,但全程 100% 占用 CPU,ASH 会记录 5 次采样;另一条 SQL 运行 30 秒,但大部分时间在等待 I/O,仅最后 2 秒在 CPU 上,ASH 只记录 2 次。从 ASH 角度看,前者更显眼;但从 AWR 角度看,后者因总执行时间长,排名反而可能更高。
判断时可以依据以下经验:
- 如果 ASH 中某个
sql_id的cpu_samples远超其他(例如达到 50 次以上),基本可以确认它就是造成 CPU 瞬时尖峰的源头。 - 接着通过
v$sql查看cpu_time / elapsed_time比值,若接近 100%,说明该 SQL 确实是纯计算型瓶颈,与 I/O 或锁无关。 - 同时留意
sql_plan_hash_value是否频繁变化。ASH 中可以通过该字段查看执行计划的历史快照,如果它在短时间内跳变多次,计划突变往往是 CPU 飙升的直接导火索。
查到高 CPU SQL 后,下一步该如何处理?
获取目标 sql_id 后,最忌讳直接拍脑袋调优。建议先做几步验证,确认它是否真的是问题根源:
- 使用
DBMS_XPLAN.DISPLAY_CURSOR('xxx', NULL, 'ALLSTATS LAST')可高效查看该 SQL 上次执行时的实际执行计划。重点关注Buffers列是否异常偏高,以及CPU列数据是否与采样次数吻合。 - 结合
client_identifier或machine字段,确认该 SQL 是否属于定时跑批任务。如果是计划内的任务,CPU 占用较高也属正常,可能无需干预。 - 分析同一
sql_id在不同时间段的cpu_samples分布,判断它是偶发性尖刺还是持续吃 CPU。后者的处理优先级更高。 - 如果发现 SQL 中包含大量
UNION ALL,或者出现驱动表选择错误的嵌套循环,容易触发 CPU 密集型全表扫描。此时,与其反复查看执行计划,不如直接检查access_predicates和filter_predicates中的条件,往往能更快锁定问题。
最后提醒一点:ASH 本身无法记录某段 PL/SQL 代码在哪个具体行号上消耗了 CPU。如果需要更细粒度的分析,需借助 DBMS_HPROF 或 oradebug short_stack 进行深入诊断。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
Redis 6.0 ACL权限控制:最小化命令授权提升安全性
Redis6 0及以上版本启用ACL时,需确认版本并修改default用户默认全开权限。按业务边界创建用户,严格绑定keypattern(如~order:*),避免漏写通配符。命令权限按输入顺序生效,推荐细粒度列举而非类别。客户端连接必须显式传递用户名,否则回退至default用户导致权限失效。
导出数据库时包含创建数据库语句的选项设置
使用mysqldump导出时,默认不包含建库语句,需添加--databases参数才能生成CREATEDATABASE和USE语句。同时,导出账号必须拥有SHOWDATABASES权限,否则会报错。注意--no-create-db参数会覆盖建库语句,导致不生成建库指令,导入前需检查目标库是否存在,并确保字符集兼容。
SQL嵌套查询使用Union与Intersect集合运算符过滤
嵌套查询中不可直接在WHERE或IN中使用UNION或INTERSECT。正确做法是将集合运算符包裹在括号内作为派生表置于FROM子句中,并显式赋予别名。对于不支持INTERSECT的数据库,可用EXISTS嵌套替代。需注意列类型对齐、别名不可省略等细节。
如何在SQL中使用BIT_COUNT函数统计二进制位数量教程
BIT_COUNT函数统计无符号整数二进制中1的个数,输入必须为非负整数,字符串或负数会导致错误。使用前应确保字段为UNSIGNED类型或通过CAST转换。该函数不走索引,频繁查询建议缓存结果。MySQL特有,其他数据库需另写逻辑。对NULL返回NULL,需用IFNULL处理。
SQL中利用JOIN查找A表不存在B表的数据
在SQL中查找A表存在而B表不存在的记录,最可靠的写法是使用LEFTJOIN并结合WHEREB idISNULL。需注意ON只用于连接逻辑,过滤条件应放在WHERE子句中;B表关联字段需要建立索引,并且要避免NULL值的陷阱。不建议使用NOTIN或EXCEPT,因为它们容易出错且性能较差。
- 热门数据榜
相关攻略
2026-07-13 07:07
2026-07-13 07:07
2026-07-13 07:07
2026-07-13 07:07
2026-07-13 07:06
2026-07-13 07:06
2026-07-13 07:06
2026-07-13 07:06
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

