先查AWR确认SQL历史执行计划是否漂移:若7天内存在多个plan_hash_value则计划已变化,否则问题不在执行路径;再验证基线状态(enabled/accepted)、参数设置及SQL文本一致性。
为什么SQL执行计划突然变差了?先看AWR里有没有历史基线
oracle优化器在统计信息变更、绑定变量窥探或版本升级后,可能生成次优执行计划。但不是所有“变慢”都该怪执行计划——得先确认:这个sql过去是否有稳定可用的计划?dba_sql_plan_baselines视图只存手动加载或自动捕获的基线,而awr(dba_hist_sqlstat + dba_hist_sql_plan)才是真实执行过的全量快照库。
查最近7天该SQL的执行计划变化趋势:
SELECT plan_hash_value, COUNT(*), MIN(sample_time), MAX(sample_time) FROM dba_hist_sql_plan p JOIN dba_hist_sqlstat s USING (sql_id, plan_hash_value) WHERE sql_id = 'your_sql_id' AND sample_time > SYSDATE - 7 GROUP BY plan_hash_value ORDER BY MIN(sample_time);
- 如果返回多个
plan_hash_value,说明计划确实漂移过;只有一个,那问题大概率不在执行路径上(比如I/O抖动、锁争用) -
plan_hash_value相同不代表性能一致——检查elapsed_time_total / executions_total是否同步飙升 - 注意
dba_hist_sql_plan默认只保留1000行/SQL的执行计划(受_cursor_plan_cache_threshold隐含参数影响),高频SQL可能被截断
怎么把AWR里的好计划抓出来,变成SQL Plan Baseline?
不能直接从AWR创建基线,必须先还原出可执行的SQL文本和完整执行计划。关键步骤是:用DBMS_XPLAN.DISPLAY_AWR拿到计划,再用DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE或LOAD_PLANS_FROM_SQLSET导入。
实操分三步走:
- 查出目标
plan_hash_value对应的sql_id和dbid(从dba_hist_sqlstat获取) - 用
DBMS_XPLAN.DISPLAY_AWR('sql_id', plan_hash_value, db_id)验证该计划是否包含完整访问路径(尤其留意filter、access谓词和cardinality预估) - 执行以下语句加载基线(需有
ADMINISTER SQL MANAGEMENT OBJECT权限):BEGIN DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE( sql_id => 'your_sql_id', plan_hash_value => 1234567890, fixed => 'YES', enabled => 'YES' ); END;
⚠️ 注意:LOAD_PLANS_FROM_CURSOR_CACHE只能加载当前还在库缓存(v$sql)里的计划。如果目标计划已老化,得先用DBMS_SQLTUNE.CREATE_SQLSET从AWR导出SQLSET,再调用LOAD_PLANS_FROM_SQLSET。
基线生效了但SQL还是没走老计划?检查ACCEPTED和ENABLED状态
一个基线能否被优化器选用,取决于两个布尔字段:enabled(是否启用)和accepted(是否通过验证)。常见陷阱是:手动加载时设了fixed => 'YES',但忘了enabled => 'YES',结果基线存在却完全不参与计划选择。
查基线状态:
SELECT sql_handle, plan_name, enabled, accepted, fixed, origin FROM dba_sql_plan_baselines WHERE sql_text LIKE '%your_keyword%';
-
enabled = 'NO':基线被禁用,ALTER BASELINE ... ENABLE可恢复 -
accepted = 'NO':该计划未通过验证(比如自动生成的演进任务失败),此时即使enabled = 'YES'也不会被选中 -
origin = 'MANUAL-LOAD'表示人工导入,'AUTO-CAPTURE'是自动捕获,后者默认accepted = 'YES'但fixed = 'NO'
如果发现accepted = 'NO'且想强制使用,可执行:DBMS_SPM.ALTER_SQL_PLAN_BASELINE将accepted设为'YES',但务必确认该计划在相似数据量下真实有效——否则会固化一个更差的计划。
为什么开了基线,执行计划还是漂移?留意OPTIMIZER_USE_SQL_PLAN_BASELINES参数
基线功能不是“开箱即用”的保险丝。实例级参数optimizer_use_sql_plan_baselines默认为TRUE,但如果被显式设为FALSE(比如在PDB级别用ALTER SYSTEM SET optimizer_use_sql_plan_baselines=FALSE),基线就彻底失效。
- 查当前值:
SHOW PARAMETER optimizer_use_sql_plan_baselines - 跨容器场景下,必须在目标PDB里确认,CDB$ROOT的设置不继承给PDB
- 即便参数为
TRUE,若SQL用了/*+ OPT_PARAM('optimizer_use_sql_plan_baselines' 'FALSE') */这样的提示,也会绕过基线 - 基线只约束计划形状(join order、access method等),不控制绑定变量具体值带来的谓词选择性变化——所以同一基线下,不同变量值仍可能导致
cardinality误判,引发实际性能波动
最常被忽略的一点:基线绑定的是SQL文本的哈希值,对空格、换行、注释敏感。应用里动态拼接SQL时,哪怕多一个空格,sql_id就不同,基线自然不匹配。


















