Oracle数据库的优化器负责为SQL语句选择执行计划,而OPTIMIZER_MODE参数直接决定了优化器的工作目标。CHOOSE与ALL_ROWS虽然都出现在这个参数的可选值列表中,但它们的内部逻辑完全不同。CHOOSE是Oracle早期版本为了兼容规则优化器和成本优化器而设计的过渡性取值,ALL_ROWS则是完全基于成本的计算方式,以完成整个结果集为优化目标。接下来从参数机制、执行计划影响和实际配置几个角度展开分析。

优化器模式参数的基础定位
在Oracle实例中,OPTIMIZER_MODE可以在系统级和会话级进行配置。系统级修改会影响之后新建的所有会话,会话级修改只对当前连接生效,而单条SQL还可以使用/*+ ALL_ROWS */或/*+ CHOOSE */这样的提示临时覆盖参数设置。优先级从高到低依次是SQL提示、会话级参数、实例级参数。因此在排查执行计划问题时,不能只看实例参数,还需要确认会话和语句是否存在额外设置。
从优化器历史来看,规则优化器(RBO)依赖固定的访问路径排序,例如优先使用唯一索引、再使用非唯一索引,而不关心表中实际数据量。成本优化器(CBO)则会根据统计信息计算I/O、CPU和临时空间等成本,选择总代价最低的方案。CHOOSE正是横跨这两者的一种模式。早期Oracle 9i数据库实例默认值为CHOOSE,而从10g开始默认值调整为ALL_ROWS,这一变化也标志着CBO成为主流。
-- 查看当前实例和会话的优化器模式 SHOW PARAMETER optimizer_mode; SELECT name, value, isdefault FROM v$parameter WHERE name = 'optimizer_mode';
CHOOSE模式的工作机制与潜在风险
CHOOSE模式的核心逻辑是动态判断:Oracle会检查SQL语句所引用的表或索引是否已经收集了统计信息。如果所有对象都没有统计信息,优化器就退回到规则优化器;如果至少一个对象存在统计信息,就启用成本优化器,并按照ALL_ROWS的目标生成执行计划。这种自动判断看似灵活,实际上给执行计划带来了极大的不确定性。
典型风险场景出现在统计信息不完整或部分收集时。假设一张订单表有5000万行数据,开发人员只对订单表执行了ANALYZE TABLE,但没有收集其关联的客户表统计信息。CHOOSE模式下,由于订单表已有统计信息,优化器会进入CBO模式,但此时客户表缺少统计信息,CBO只能使用默认的估算行数。估算值与真实数据分布偏差较大时,可能错误选择全表扫描或笛卡尔连接,导致性能急剧下降。更隐蔽的是,同一SQL在不同时间运行,可能因为统计信息的有无而切换RBO和CBO,执行计划时好时坏,难以复现和定位。
规则优化器本身也有明显局限。它按照固定的访问路径评分规则选择索引,比如单列唯一索引总是优先于非唯一索引,却完全不考虑索引的选择性。当表中某列的唯一索引包含大量空值或倾斜数据时,规则优化器仍然会优先使用该索引,而成本优化器则可能判断全表扫描成本更低。这种脱离数据特征的决策方式,是Oracle逐步淘汰CHOOSE和RULE的重要原因。
ALL_ROWS模式的优化目标与执行计划特征
ALL_ROWS模式要求优化器以最小化返回完整结果集的总成本为目标。也就是说,如果SQL最终要返回100万行,优化器会优先选择能够最快完成全部100万行读取的执行路径,而不是最快返回前10行。这意味着在ALL_ROWS下,大表的全表扫描、哈希连接、并行查询往往更受青睐,因为它们能够以较高的吞吐量完成批量数据处理。
这种目标与FIRST_ROWS_n完全不同。后者主要面向交互式应用,让用户尽快看到第一屏数据。比如一个ERP系统中的分页查询,如果优化器目标是FIRST_ROWS_10,可能会选择嵌套循环和索引扫描,以便快速返回前10条;而同样的SQL使用ALL_ROWS时,如果优化器估算要返回几万行,则可能选择哈希连接并并行全表扫描。对于批处理报表、数据抽取和历史数据清理任务,ALL_ROWS通常更合适。
-- 使用提示强制单条SQL采用ALL_ROWS模式 SELECT /*+ ALL_ROWS */ e.employee_id, e.last_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE e.hire_date >= DATE '2015-01-01'; -- 使用EXPLAIN PLAN查看执行计划 EXPLAIN PLAN FOR SELECT /*+ ALL_ROWS */ e.employee_id, e.last_name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE e.hire_date >= DATE '2015-01-01'; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
在ALL_ROWS模式下,统计信息的准确性变得更加重要。CBO依赖表行数、块数、列不同值数量、数据分布直方图以及系统统计信息来计算各执行路径的成本。如果统计信息过期或缺失,ALL_ROWS可能会选择比CHOOSE时代的RBO更差的计划。例如某个客户表长期未收集统计信息,优化器误认为只有几十行,于是选择嵌套循环;实际表中有几千万行,执行时产生大量逻辑读,CPU飙升。因此使用ALL_ROWS时,必须配合稳定的统计信息维护机制。
从CHOOSE迁移到ALL_ROWS的配置与验证
对于仍在使用CHOOSE的老系统,升级前应该先评估执行计划基线。直接修改参数可能导致部分SQL执行计划大幅变化。建议先在测试环境将OPTIMIZER_MODE从CHOOSE改为ALL_ROWS,并利用AWR报告、SQL Trace或v$sql_plan对比高负载SQL的执行计划变化。对于变差的SQL,可以通过SQL计划基线、SQL Profile或调整统计信息来稳定。
修改实例级参数的SQL如下:
-- 将实例优化器模式改为ALL_ROWS ALTER SYSTEM SET optimizer_mode = all_rows SCOPE=both; -- 当前会话立即生效 ALTER SESSION SET optimizer_mode = all_rows;
参数修改后,要重点检查核心业务表是否已有及时更新的统计信息。Oracle 10g开始提供自动统计信息收集任务,但很多老系统升级后可能仍然沿用旧的ANALYZE脚本,甚至禁用了自动任务。建议使用DBMS_STATS包定期收集统计信息,并对倾斜列生成直方图。下面是一个常用的表级统计信息收集示例:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SCOTT',
tabname => 'EMPLOYEES',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO',
cascade => TRUE
);
END;
统计信息收集策略稳定后,还需要关注绑定变量窥探的影响。即使优化器模式固定为ALL_ROWS,CBO在处理绑定变量时仍可能因为第一次硬解析看到的变量值而产生倾斜计划。针对这种情况,可以使用_optim_peek_user_binds参数关闭绑定变量窥探,或使用SQL计划基线锁定执行计划。另外,升级后建议保留一段时间的性能监控,一旦发现个别SQL出现异常,可通过DBMS_SPM加载旧计划,避免业务中断。
总之,CHOOSE是Oracle优化器演进过程中的历史产物,它把RBO和CBO的判断交给统计信息是否存在,实际使用中容易引发执行计划不稳定。ALL_ROWS则是现代Oracle数据库的标准成本优化模式,追求完整结果集的处理吞吐量。对于大多数批处理和报表场景,显式使用ALL_ROWS并维护准确的统计信息,能够获得更稳定、更可预测的执行计划。

