Oracle 优化器
什么是优化器
优化器是Oracle中的一个核心模块,它的作用是为用户输入的SQL选择一个它计算出来的最高效的执行计划。SQL语句在Oracle中的执行过程如下图所示:
基于规则优化器RBO
基于规则的优化器现在基本上已经不怎么用了,这里只做简单介绍。
基于规则的优化器是将一系列规则固定在系统中,给 每个执行路径定一个等级。最低是1最高是15 。如:等级1对应的是:single row by rowid (通过rowid访问数据),等级15则对应的是:full table scan (全表扫描) 。当SQL执行的时候,有不同的执行路径可以选择,那么就从该SQL的执行计划中选择一条等级值最低的路径作为其执行计划。
RBO的缺点
由于其选择执行计划的方式比较死板,所以会有很多缺陷(相对于CBO)。
1.在使用RBO的时候,一旦执行计划出了问题,很难对其做调整。
2.使用RBO时,SQL的写法,甚至是SQL中涉及对象在SQL文本出现的先后次序都可能会影响RBO对执行计划的选择。
3.Oracle 数据库很多好的特性、功能RBO都无法兼容。如:目标SQL中涉及的对象有ITO(index organized table);使用了哈希链接、星型链接、函数索引等。
由于诸多原因,RBO选择出来的执行计划不一定是最优的执行计划(例如:建立索引的字段都是同样的值,那么使用索引效率不如全表扫描效率高。但是依然会通过索引去取值)。所以,Oracle 10g 开始,就改为使用CBO了。10g 以后的版本,如果想要使用RBO。需要通过修改优化器模式,或使用RULE Hint来继续使用RBO。
在当前session中使用RBO:
ALTER SESSION SET OPTIMIZER_MODE='RULE'
基于成本的优化器CBO
什么是CBO
为了解决RBO由于硬编码导致执行计划不准确的问题,从Oracle7开始,Oracle就引入了CBO。CBO在选择执行计划的时候,所用的判断原则为成本,CBO会从诸多的执行计划中选择一条成本最小的执行路径作为其执行计划。各条执行路径成本是根据目标的SQL语句所涉及的表、索引、列等相关对象的统计信息计算得出的。这些统计信息存储在Oracle的数据字典里,并且从多个角度描述了Oracle数据库中相关对象的数据量、数据分布情况等信息。
Oracle在选择执行计划的时候,会根据这些统计信息算出相关执行步骤对应的IO、CPU和网络资源消耗的值,然后根据这些消耗的成本选择一条执行路径。
CBO相关概念_Cardinality
Cardinality 是CBO特有概念,指的是指定结果集的行数。与SQL执行计划的某个执行步骤相对应。用于对目标SQL的某个执行步骤的执行结果包含的记录数进行估算。对于整个SQL,真是最终执行结果包含的记录数。这个值越大,标识着结果集所消耗的IO越多。对应的成本一般也会越大。这个执行路径的总的成本也会越大。
CBO相关概念_可选择率
可选择率,也是CBO特有的概念。指的是加上WHERE条件后返回的结果集的数量与不加条件返回的原始结果集的记录数比值。这个值的取值范围是0~1 计算公式如下:
施加谓语条件得到的记录数
可选择率 = ————————————————–
未施加谓语条件返回的原始记录数
可选择率和成本的估算关系是:可选择率越大,对应的结果集行数也会越大。可选择率越大, 执行步骤估算的成本值也就越大,这个执行路径对应的总成本也会越大。
CBO相关概念_可传递性
可传递性,依然是CBO特有概念。CBO在拿到SQL语句后的第一件事,就是对SQL进行等价改写。即在SQL上添加根据现有条件推导出来的新的谓语条件,,我们SQL在进行执行路径选择的时候,就会将推导出来的谓词条件对应的执行步骤也进行计算。可能会得到一个成本比原来的谓词条件成本更低的执行路径。从而选择更优的执行计划。关于可传递性,分为三种情况:
1.简单谓词传递
例如原SQL为:select * from t1,t2 where t1.c1= t2.c1 and t1.c1 = 10 这条SQL,CBO就会添加一个 t2.c1=10的条件,将SQL语句变为:select * from t1,t2 where t1.c1= t2.c1 and t1.c1 = 10 and t2.c1=10,改写前后的SQL语句谓词条件是等价的。我们通过 t1.c1=t2.c1 ,t1.c1= 10 很容易推导得出 t2.c1 = 10
2.连接谓词传递
例如:原SQL为: select * from t1,t2,t3 where t1.c1= t2.c1 and t2.c1 = t3.c1 ,CBO 在优化的时候,会在谓词条件中添加一个 t1.c1= t3.c1 的条件,将SQL语句变为:select * from t1,t2,t3 where t1.c1= t2.c1 and t2.c1 = t3.c1 and t1.c1=t3.c1,同理,这样的修改也是等价的。
3.外连接谓词传递
例如:原SQL为:select * from t1,t2 where t1.c1= t2.c1(+) and t1.c1 = 10 ,对于该SQL语句CBO在优化的时候会在谓词条件中加上t2.c1(+)=10 这个条件。将SQL改为:select * from t1,t2 where t1.c1= t2.c1(10) and t1.c1 = 10 and t2.c1(+) =10 这同样是等价的改写。
简单实例
这里通过一个简单的实例对CBO可传递性做个测试:
创建t1表和t2表:
create table t1(c1 number,c2 varchar2(10)); CREATE table t2(c1 number,c2 VARCHAR2(10));
t2中创建索引:
CREATE index idx_t2 on t2 (c1);
往表中随便插入一些数据,然后执行SQL:
SELECT t1.C1,t2.c2 FROM T1,t2 WHERE t1.C1 = t2.C1 and t1.C1 =10;
查看执行计划
Plan hash value: 794674300 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 33 | 2 (0)| 00:00:01 | | 1 | MERGE JOIN CARTESIAN | | 1 | 33 | 2 (0)| 00:00:01 | |* 2 | TABLE ACCESS FULL | T1 | 1 | 13 | 2 (0)| 00:00:01 | | 3 | BUFFER SORT | | 1 | 20 | 0 (0)| 00:00:01 | | 4 | TABLE ACCESS BY INDEX ROWID BATCHED| T2 | 1 | 20 | 0 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | IDX_T2 | 1 | | 0 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("T1"."C1"=10) 5 - access("T2"."C1"=10) Note ----- - 'PLAN_TABLE' is old version - dynamic statistics used: dynamic sampling (level=2)
在执行计划id为5的执行计划中可以看到Oracle执行计划通过索引进行了范围扫描。而这个步骤所对应的查询条件是 access(“T2”.”C1″=10) 这个条件。说明执行计划生成的谓词条件中有 t2.c1=10 这个谓词条件,但是原SQL中并不包含该条件。说明CBO确实对SQL语句进行了改写。而改写后的SQL条件可以使用t2表的索引,这有利于提高SQL语句的执行效率。改写后的SQL:
SELECT t1.C1,t2.c2 FROM T1,t2 WHERE t1.C1 = t2.C1 and t1.C1 =10 and t2.c1 = 10;
CBO的局限性
CBO解决了RBO的先天缺陷,并且越来越智能,但是仍然有很多可以优化的地方,如:
1.CBO认为SQL的where条件出现的各个列相互独立,没有关联。CBO对于每个列单独进行成本计算,然后通过执行成本来选择执行计划,但是很多列有时候是相关联的,单独计算可能会导致执行计划出现偏差,选错执行计划。
2.CBO选择执行计划的时候,只考虑当前SQL。 CBO假设所有SQL都是单独执行,互不干扰的,但是,很多时候,执行目标SQL需要的数据块、索引叶子块等数据可能已经被缓存到了Buffer Cache中了,单独计算可能也会导致选择不到最优的执行计划。
3.CBO在直方图统计方面有诸多限制。
4.CBO在处理多表关联的SQL时,可能会漏选执行计划。在关联的表越多的情况下,执行路径的总数量也会成倍数增长。例如:假如一个表只有1个执行分支可供选择,那么两个表(t1,t2)就是有 t1-t2(先执行t1条件,然后筛选t2条件) 和 t2-t1(先执行t2条件,然后筛选t1条件) 两个执行路径。如果是三个表(t1,t2,t3)那么就会有6种组合,如果是四个表那么就有24种组合,其计算方式为:表格数量的阶乘。那么如果有十几个二十个表的时候甚至会有百亿级别的执行路径,如果把所有执行路径全部执行一遍,那么,选择器就会耗费相当巨大的时间,所以,Oracle在执行CBO的时候,会有个最大选择路径的隐含参数(_optimizer_max_permutations)。不管有多少执行计划,Oracle只会考虑_optimizer_max_permutations限制的可能。那么,如果最优的执行计划,不再该范围内,就会漏选。
oracle 优化器模式的切换
通过如下SQL可以切换当前session的优化器模式:
ALTER SESSION SET OPTIMIZER_MODE='RULE'
其中,OPTIMIZER_MODE的各个可能值如下:
RULE:使用RBO来解析目标SQL。
CHOOSE:9i 的默认值,表示在执行SQL语句时,选择那个RBO还是CBO取决于对象是否具有统计信息。
FIRST_ROWS_n(n= 1,10,100,1000) :使用CBO进行执行,并且在执行时,以最快的响应速度,返回前n条记录。
FIRS_ROWS 9i 中过时的参数。表示同时使用RBO和CBO 。
ALL_ROWS: 10g 及以后的默认值,表示使用CBO来解析目标SQL,且挑选执行计划时,侧重计算执行路径成本值。