查自适应执行计划必须用dbms_xplan.display加+adaptive修饰符,否则只显示编译时默认分支;+adaptive是强制项,缺之则无法看到子计划、决策点及实际激活路径,输出中带“rows marked '-' are inactive”的行表示未激活分支,note区须含“this is an adaptive plan”才说明启用成功。
查自适应执行计划必须用 dbms_xplan.display 加 +adaptive 修饰符
默认的 dbms_xplan.display 只显示“编译时选定”的默认分支,根本看不到自适应切换逻辑。不加 +adaptive,你看到的永远是静态计划,哪怕实际运行中已切到另一条路径。
正确写法示例:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY( format => 'basic +predicate +note +adaptive' ));
关键点:
-
+adaptive是强制项,缺了就看不到子计划、决策点和实际激活路径 - 输出里带
rows marked '-' are inactive的行,代表该分支在本次执行中未被激活 - 注意
Note区域是否含this is an adaptive plan—— 没这句说明压根没启用自适应(可能因 optimizer_features_enable 或 _optimizer_adaptive_plans=FALSE)
从 ASH 找出真正执行了哪条子计划
v$active_session_history 不存计划结构,但能告诉你「哪个操作节点实际花了时间」。自适应计划里多个子计划共用同一组 operation_id,但执行路径不同,ASH 中的 sql_plan_operation 和 sql_plan_options 会暴露真实走法。
例如:一个自适应联接在 plan 中有 NESTED LOOPS 和 HASH JOIN 两个分支,ASH 样本里若反复出现:
sql_plan_operation = 'HASH JOIN' AND sql_plan_options = 'JOIN'
就说明运行时选了 HASH 分支,而非默认的 NL 分支。
实操建议:
- 用
sql_id关联gv$active_session_history和dba_hist_sql_plan,确认plan_hash_value是否随时间变化(自适应切换会导致 plan_hash_value 改变) - 过滤条件加
session_state = 'ON CPU',排除等待事件干扰,聚焦真实计算耗时路径 - RAC 环境必须用
gv$active_session_history,否则可能只看到 coordinator 节点的默认计划,漏掉 slave 节点实际执行的分支
time_waited 在自适应场景下反而容易误导
自适应执行计划的决策发生在执行过程中(比如前几万行扫描后触发切换),但 time_waited 记录的是等待事件耗时,不是操作本身耗时。对自适应分支来说,真正关键的是 sql_exec_start 到 sql_exec_id 变化的时间差,以及各 operation_id 在 ASH 中的样本密度。
容易踩的坑:
- 只看
event IN ('PX Deq: Execution Message', 'cursor: pin S wait on X')—— 这些是协调开销,不是子计划选择依据 - 用
COUNT(*)统计某 operation 的样本数却不关联sql_exec_id—— 同一sql_id多次执行会混在一起,看不出单次执行内是否发生切换 - 忽略
in_parse和in_hard_parse字段:如果 ASH 样本里大量出现in_parse = 'Y',说明自适应决策引发频繁重解析(常见于绑定变量窥视失效)
结合 DBA_HIST_SQLSTAT 看自适应是否真带来收益
自适应不是万能的。有些场景下切换反而更慢(比如小数据集切 HASH JOIN,或并行度不足时广播变瓶颈)。判断它有没有起作用,不能只看执行计划,要看真实性能指标。
查法:
SELECT sql_id, plan_hash_value, SUM(elapsed_time_delta)/1000000 elapsed_sec, SUM(executions_delta) execs FROM dba_hist_sqlstat WHERE sql_id = '&your_sql_id' AND snap_id BETWEEN &start_snap AND &end_snap GROUP BY sql_id, plan_hash_value;
如果同一 sql_id 出现多个 plan_hash_value,且对应 elapsed_sec/execs 差异显著(比如 200ms vs 800ms),就说明自适应在不同数据分布下做出了不同选择,且效果可量化。
注意:
-
DBA_HIST_SQLSTAT里的plan_hash_value是最终执行计划的哈希,不是编译计划 —— 它才是自适应落地的真实证据 - 别直接对比 AWR 报告里的 TOP SQL 平均耗时,那会把不同 plan_hash_value 混在一起平均,掩盖切换效果
- 如果发现某个
plan_hash_value执行次数极少但耗时极高,大概率是自适应误判导致的长尾延迟,需检查谓词选择性或动态采样是否生效
自适应执行计划的诊断难点不在“能不能看到”,而在“怎么确认它实际走了哪条路、为什么走这条路、走对了没有”。ASH 提供的是执行时的现场痕迹,不是计划说明书——得靠 operation、exec_id、plan_hash_value 三者交叉印证,而不是盯着某一个字段下结论。











