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

作者:技术拾光时间:2026-06-23 08:51:01浏览:273

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_PLAN 和 DBA_HIST_SQLSTAT。

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

具体怎么做?三步走:

  • 先查出目标 SQL 的 sql_id:SELECT 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。总之,未雨绸缪永远比事后补救省心。