游乐游手机版
首页/数据库/文章详情

Oracle SQL性能突然下降 AWR历史计划回滚与SPM快速修复指南

时间:2026-06-22 11:45
Oracle11g执行计划变差时,可通过AWR历史计划借助SPM回滚:先查SQL的sql_id,确认历史plan_hash_value,再用DBMS_SPM LOAD_PLANS_FROM_AWR导入历史计划生成基线。需确保参数optimizer_use_sql_plan_baselines为TRUE且SQL文本完全一致(含大小写与空格),否则计划不生效。注

执行计划变差后,为什么不能直接改统计信息?

先说个大实话:很多人一遇到执行计划“漂移”、跑偏,第一反应就是——赶紧收集下统计信息吧。但这事儿真没那么简单,也未必管用。

说到底,核心原因无非两个:一是收集统计信息本身就很耗时,尤其对大表来说,跑一轮 dbms_stats.gather_table_stats 可能十分钟、半小时就过去了,可业务这会儿已经卡在那儿,你哪有时间等它跑完?二是——这也是更隐蔽的一点——问题可能根本就不是统计信息引起的。比如在 11g 环境中,你开了 optimizer_capture_sql_plan_baselines,结果新计划还没来得及验证就被自动启用了,这叫“计划漂移”,跟统计信息一毛钱关系都没有。19c 之后还有 optimizer_adaptive_features 这个变量在搅局,但更早的版本也一样有各种“坑”。

Oracle 11g中SQL执行计划变差如何快速回滚_利用AWR历史计划执行SPM导入

怎么从 AWR 中捞出历史好计划并导入 SPM?

AWR 本身其实不存完整的执行计划,它只存了一个 plan_hash_value 和 SQL 文本。真正能导出可复用计划的,是藏在 AWR 快照背后的 DBA_HIST_SQL_PLANDBA_HIST_SQLSTAT

这里有两个工具可以用:一个是 DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE,另一个是更稳妥的 DBMS_SPM.LOAD_PLANS_FROM_AWR。但注意,后者在 11.2.0.2 及以上版本才支持,而且有个硬前提——这个 SQL 在 AWR 快照周期内必须确实被采样过。也就是说,如果好计划只出现过一次,恰好没被 AWR 抓到,那就没法用这个函数捞出来。

具体怎么做?三步走:

  • 先查出目标 SQL 的 sql_idSELECT sql_id, sql_text FROM v$sql WHERE sql_text LIKE '%your_condition%';
  • 确认该 sql_id 在 AWR 中有历史计划:SELECT DISTINCT plan_hash_value FROM dba_hist_sql_plan WHERE sql_id = 'xxx';
  • 最后,用 DBMS_SPM.LOAD_PLANS_FROM_AWR 把那个好计划的 plan_hash_value 导进来:
BEGIN
  DBMS_SPM.LOAD_PLANS_FROM_AWR(
    begin_snap => 12345,
    end_snap   => 12346,
    basic_filter => 'sql_id = ''abc123'' AND plan_hash_value = 1234567890'
  );
END;

导入后计划没生效?检查这几个硬条件

很多人以为把计划导入 SPM 就完事了,结果发现新计划根本没生效。这背后有几个经常被忽略的“硬门槛”:

  • 参数 optimizer_use_sql_plan_baselines 必须为 TRUE。虽然默认就是 TRUE,但有些环境为了排查问题被人为关掉了,这点得确认。
  • SQL 文本必须完全一致。注意,不是“差不多一致”——尾部空格、换行符、大小写差异,都会导致匹配失败。这真是最烦人的坑之一。
  • 如果 SQL 使用了绑定变量,而历史计划是在不同绑定值下生成的,且 force_matching 模式没开启,那 SPM 也可能不匹配。
  • 执行 SQL 时如果加了 /*+ NO_SQL_PLAN_BASELINE */ 提示,那会直接绕过基线,计划自然不生效。

coe_load_sql_profile.sql 和 SPM 基线的区别在哪?

有些 DBA 会用 coe_load_sql_profile.sql 来“打补丁”——它本质是创建一个基于 HINT 注入的 SQL Profile,优先级比 SPM 基线还高。但这才是个大问题:Profile 属于“补丁式”修复,是会话级或语句级的 hint 封装,不参与计划演化,也不支持自动验证和演进。而 SPM 基线是存储在 sys.sqlobj$ 等正式对象中的计划控制机制,它能自动验证、演进和禁用,是更正规的路子。

所以建议是:生产环境优先走 SPM。Profile 只用于紧急兜底,或者跨版本迁移这种特殊场景——毕竟,你总不能让对方改 SPM 配置吧。

还有个容易被忽略的点:AWR 快照间隔默认是 60 分钟。如果好计划只在某次短时负载中间出现过,但没被 AWR 采样到,那 LOAD_PLANS_FROM_AWR 就查不到它。这时候只能退而求其次,从 v$sql_plan 或者 cursor cache 里抓,或者依赖之前手动捕获的 baseline。总之,未雨绸缪永远比事后补救省心。

来源:https://www.php.cn/faq/2683697.html
上一篇SQL Server窗口函数计算移动平均的实用指南 下一篇如何利用SQL Server计算列索引实战高效提升Join关联速度
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Redis 7.0增量AOF重写RDB前导码配置详解
数据库 · 2026-07-02

Redis 7.0增量AOF重写RDB前导码配置详解

先说一个几乎所有人都踩过的典型误区:很多人把 aof-use-rdb-preamble yes 当作开启“增量重写”的开关。实际上,这个配置只干了一件事——让重写后的 AOF 文件头部带上 RDB 快照。它解决的是加载速度问题,跟“增量重写”本身的概念压根不是一回事。真正的增量重写,依赖的是 Red

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践
数据库 · 2026-07-02

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践

直接在Tornado里用SQLAlchemy同步执行SQL,结果就是阻塞IOLoop,所谓“异步框架里写同步数据库代码”,等于白搭。安全执行的关键不是“怎么写SQL”,而是“怎么不卡住事件循环”。 为什么不能在RequestHandler里直接调用session execute() 因为sessio

利用SQL触发器实现在INSERT数据时自动同步到审计表
数据库 · 2026-07-02

利用SQL触发器实现在INSERT数据时自动同步到审计表

先说结论:可以用触发器把 INSERT 数据同步到审计表,但必须用 AFTER INSERT,并且审计表的字段顺序、类型、字符集得和源表严格一致。否则,轻则写入错位、数据截断,重则直接报错、丢数据。下面把这些坑一个一个掰开说。 能,但必须用 AFTER INSERT,且审计表字段顺序、类型、字符集要

如何用SQL编写按不同工作日统计员工出勤率
数据库 · 2026-07-02

如何用SQL编写按不同工作日统计员工出勤率

在实际业务中,统计不同工作日的出勤率是HR系统里的高频需求。如果直接按日期函数分组,很容易掉进语言环境、索引失效或分母口径的坑里。下面就来拆解具体的实现要点。 必须用 CASE WHEN 将日期映射为固定 weekday 标签(如 Mon )再分组,避免语言环境导致的分组断裂;需过滤 DOW IN

Spring Boot 3动态拼接SQL为何引发严重安全漏洞
数据库 · 2026-07-02

Spring Boot 3动态拼接SQL为何引发严重安全漏洞

SQL注入漏洞的核心成因,本质上是因为用户输入直接参与了SQL语句的字符串拼接,而未采用参数化绑定机制。在MyBatis中使用${}、QueryWrapper中调用apply()与last()、JPA的@Query注解进行拼接等操作,都会绕过PreparedStatement的安全防护。动态字段必须