Oracle自适应查询优化是一组让优化器不再只依赖硬解析时估算值的功能集合。传统优化器在解析阶段根据数据字典统计信息、优化器参数和成本模型一次性确定执行计划,之后即使实际运行结果与预估严重偏离,执行计划也不会改变。数据倾斜、列相关性、复杂谓词等情况常常导致基数估算错误,进而选出低效的嵌套循环连接或错误的表连接顺序。Oracle从12c开始引入自适应查询优化,将一部分决策延后到运行时,并利用执行反馈修正后续计划。其核心组件包括自适应计划、自动重优化、SQL计划指令和动态统计信息。

一、自适应计划:在单次执行内部切换连接方式
自适应计划解决的是硬解析阶段无法确定最优连接方法的问题。优化器会生成一个包含多个子计划的执行计划,并在关键位置放置统计收集器行源。统计收集器会缓冲来自子计划的行,同时统计实际返回行数。如果实际行数低于阈值,优化器继续使用原有的嵌套循环连接;如果行数超过阈值,优化器会在缓冲结束后切换到哈希连接。这种切换发生在单次执行过程中,语句不会因为切换而重新解析。
例如,一个订单表与订单明细表连接时,优化器可能无法准确估计某个客户分组下的明细数量。自适应计划可以先统计订单表过滤后的实际行数,再决定是否改用哈希连接。下面的执行计划片段展示了统计收集器和两个可选连接路径:
-------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 12 | | 1 | HASH JOIN | | 100 | 25000 | 12 | | 2 | NESTED LOOPS | | 100 | 25000 | 12 | | 3 | STATISTICS COLLECTOR | | | | | | 4 | TABLE ACCESS FULL | ORDERS | 100 | 10000 | 5 | | 5 | TABLE ACCESS BY INDEX ROWID| ITEMS | 1 | 150 | 1 | | 6 | INDEX RANGE SCAN | ITEM_IDX | 1 | | 1 | --------------------------------------------------------------------------
从计划中可以看到,优化器保留了嵌套循环连接和哈希连接两个分支,统计收集器位于驱动表扫描之后。实际执行时如果ORDERS表返回行数远大于预估,优化器会放弃索引扫描路径,转而使用哈希连接,从而避免大量单块读。自适应计划还支持并行分发方式的切换,例如从HASH分发切换到BROADCAST分发,以缓解并行服务器进程间数据倾斜。
不过自适应计划并非适用于所有连接方法。动态切换主要支持嵌套循环连接与哈希连接之间的选择,以及并行模式下的数据分发方式。排序合并连接、位图连接等通常不在自适应范围内。此外,统计收集器需要缓冲行,额外的内存和CPU开销必须纳入评估。对于短事务或小结果集,自适应计划带来的延迟可能超过收益。因此Oracle默认启用该特性,但在OLTP环境中如果出现大量高并发短查询,可以评估通过optimizer_adaptive_plans参数关闭或降低影响。
二、自动重优化:跨执行修正优化器估算
自动重优化处理的是跨执行层面的估算矫正问题。如果某条SQL语句第一次执行时优化器估算严重失准,Oracle会在执行结束后记录实际统计信息,并在下一次硬解析时修正基数估算、连接顺序或连接方法。与自适应计划不同,自动重优化不会改变正在执行的那次计划,而是在后续执行时生成新计划。
自动重优化包含两个主要部分:统计信息反馈和性能反馈。统计信息反馈关注过滤条件、连接条件和聚合操作的估算偏差。优化器会比较估算行数与实际行数,如果偏差超过一定倍数,则在下一次解析时调整。性能反馈则在并行查询或串行查询中检测到估算成本与实际性能严重不匹配时,引入额外的动态采样或调整并行度。这些修正信息可以持久化到SQL计划基线和SQL计划指令中,也可以只用于下一次优化。
可以通过V$SQL视图观察自动重优化行为。例如下面的查询可以找出哪些语句发生过重新优化:
SELECT sql_id, child_number, is_reoptimizable, is_resolved_adaptive_plan FROM v$sql WHERE is_reoptimizable = 'Y' ORDER BY last_active_time DESC;
IS_REOPTIMIZABLE为Y表示该游标因为自动重优化被标记,下一次执行可能重新优化。IS_RESOLVED_ADAPTIVE_PLAN标识是否已经解析出自适应计划。自动重优化的触发条件包括估算行数偏差超过一定倍数、运行时间偏差明显、绑定变量窥视导致计划不稳定等。对于使用绑定变量的语句,自动重优化还能在绑定变量值变化时收集多组执行统计,帮助优化器选择更稳健的计划。
三、SQL计划指令:持久化的优化器学习成果
SQL计划指令是自适应查询优化中最容易忽视但影响深远的组件。它通过持久化对象记录优化器在多次执行中发现的统计信息缺陷,例如某个列组合存在函数依赖、某列直方图缺失、连接条件之间隐含相关性等。优化器在后续解析SQL时自动读取这些指令,并决定是否进行动态统计信息收集或调整成本估算。
当优化器发现某个表的某列过滤条件估算错误时,会创建一条SQL计划指令,要求以后对该列使用动态统计信息。指令最初可能只针对单个SQL,随着更多语句受益,它会提升为全局指令。查看SQL计划指令可以使用DBA_SQL_PLAN_DIRECTIVES视图:
SELECT directive_id, type, state, created, last_modified FROM dba_sql_plan_directives WHERE state = 'USABLE' ORDER BY last_modified DESC;
用户也可以查看DBA_SQL_PLAN_DIR_OBJECTS了解指令涉及的列。SQL计划指令存储在SYSAUX表空间中,长期运行的系统可能积累大量指令。Oracle提供DBMS_SPD包用于管理这些指令,例如DBMS_SPD.DROP_SQL_PLAN_DIRECTIVE可以删除不再需要的指令。默认情况下,指令数量受内部限制控制,当达到上限后旧的指令会被自动回收。
需要留意的是,SQL计划指令虽然能提升未来解析的准确性,但每条带有指令的SQL在硬解析时可能触发额外的动态采样,增加解析时间。如果某类语句对解析延迟敏感,可以结合OPTIMIZER_DYNAMIC_SAMPLING和OPTIMIZER_ADAPTIVE_STATISTICS参数调整行为。
四、监控与调优:如何在生产环境管理自适应特性
自适应查询优化不会自动解决所有性能问题,它同样需要被监控和治理。首先需要确认运行计划中是否存在自适应切换。可以在V$SQL_PLAN中查找统计收集器行源,或者检查V$SQL的IS_RESOLVED_ADAPTIVE_PLAN字段。下面的查询可以统计当前库缓存中自适应计划的比例:
SELECT is_resolved_adaptive_plan, COUNT(*) FROM v$sql WHERE is_resolved_adaptive_plan IS NOT NULL GROUP BY is_resolved_adaptive_plan;
如果发现大量自适应计划频繁切换,需要分析是否存在统计信息过期、直方图缺失或系统负载过高导致收集器开销放大等问题。可以通过收集更准确的优化器统计信息、创建扩展统计信息或使用SQL计划基线来减少对运行时切换的依赖。对于数据仓库和复杂报表,自适应查询优化通常能带来明显收益;对于高并发、短事务、计划必须绝对稳定的场景,则可能需要关闭部分自适应功能。
关键参数包括optimizer_adaptive_plans、optimizer_adaptive_statistics和optimizer_dynamic_sampling。将optimizer_adaptive_plans设为FALSE会禁用自适应计划,但自动重优化和SQL计划指令仍可工作;将optimizer_adaptive_statistics设为FALSE会关闭统计信息反馈和SQL计划指令的创建。不同Oracle版本对这些参数的控制范围略有差异,生产环境修改前应进行充分测试。
自适应查询优化是Oracle优化器从静态估算走向动态学习的重要一步。理解自适应计划、自动重优化和SQL计划指令的区别,能够帮助DBA更准确地判断SQL性能问题的根源,而不是简单地将慢解析归咎于优化器。合理利用这些机制,配合完善的统计信息维护策略,可以在复杂SQL场景中显著降低执行计划偏差带来的风险。
Oracle自适应查询优化自适应执行计划SQL性能调优修改时间:2026-08-20 00:52:02