ORACLE CBO RBO 优化器介绍与区别

目录

一、什么是优化器

二、优化器的模式

三.RBP 与CBO的比较


一、什么是优化器


为 SQL  生成最佳的执行计划,比如什么时候是全表扫描 (FTSfulltable scan) ,什么时 候是索引范围搜索 (Index Range Scan) ,或是全索引扫描 (INDEX fastfullscan ,INDEX_FFS) ;
如果是表于表之间连接的时候,它会负责让表之间以一种什么样子的形式来关联,比如 hash_join  还是 nested loops  或者是 merge join 。这些因素直接决定了 SQL  的执行效  率。
1 、优化器的种类
      Rule Based Optimizer  (RBO) 基于规则  (8i  之前使用的)
      Cost Based Optimizer  (CBO) 基于成本,或者讲统计信息
RBO:   自 ORACLE6  版以来被采用,有着一套严格的使用规则,只要你按照它去写 SQL  语 句,无论数据表中的内容怎样,也不会影响到你的“执行计划”,也就是说对数据不“敏感”; CBO  计算各种可能“执行计划”的“代价”,即 cost ,从中选用 cost  最低的方案,作为实 际运行方案。各“执行计划”的 cost  的计算根据,依赖于数据表中数据的统计分布,ORACLE 数据库本身对该统计分布并不清楚,必须要分析表和相关的索引 (使用 ANALYZE  命令) , 才能搜集到 CBO  所需的数据。  优化器在分析 SQL  语句时,所遵循的是 Oracle   内部预定的 一些规则。比如我们常见的,当一个 where  子句中的一列有索引时去走索引。
CBO  方式:它是看语句的代价(Cost),这里的代价主要指 Cpu  和内存。优化器在判断是否 用 这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行 的 长度等信息。这些统计信息起初在库内是没有的,是做 analyze  后才出现的,很多的时侯过 期统计信息会令优化器做出一个错误的执行计划, 因些应及时更新这些信息。


二、优化器的模式


基于规则的优化器以 ORACLE 为中心,基于成本的优化器以数据为中心。数据库锁采用的 优化其模式可以用过初始化参数 optimizer_mode 来设置。下面列出了集中可能使用的优化 器模式:
1) first_rows_n(n=1、10、100、1000)
first_rows_n(n=1、10、100、1000)可以是 first_rows_1、first_rows_10、first_rows_ 100、 first_rows_1000 中的任意一个值,他表示 oracle 在解析目标 sql 时,oracle 会使用 cbo 来解析 目标 sql,且此时 cbo 在计算各条执行路径的成本时的侧重点在于以最快相应速度返回前 n 条数据。
2) first_rows
first_rows 是一个在 oracle 9i 中就过时的一个参数,他表示 oracle 在解析目标 sql 时会联合使用 cbo 和 rbo 。在大部分情况下,oracle 还是会选用 cbo 作为解析目标 sql,此时 oracle 的侧重点是 以最快的相应速度返回前 n 行。在一些特俗情况下,oracle 会选用 rbo 来解析目标 sql而不考虑成 本。比如当 optimizer_mode 为 first_rows 时有一个内置的规则,就是 oracle 如果发现能用相关 索引来避免排序,则 oracle 就会选择该索引所对应的路径而不考虑成本值。
3) all_rows
all_rows 是 oracle 10g 及以后 oracle 的版本中 optimizer_mode 的默认值,它表示 oracle 会使 用 cbo 来解析目标 sql,此时 cbo 计算目标 sql 的各个执行路径的成本以最小的资源消耗以及最佳吞吐量。当 optimizer_mode 为 first_rows 时,cbo 计算成本侧重于最快响应时间;当 optimizer_mode 为 all_rows 时,cbo 计算成本侧重于最佳吞吐量。
 

三.RBP 与CBO的比较

1.RBO   比较呆板,只要有索引,就是用索引,无论效率高低。
create table t as select 1 id,object_name from dba_objects;
update t set id = 99 where rownum=1;
commit;
select id,count(*) from t group by id;
ID       COUNT(*)
---------- ----------


99
1


1
72407

create index t_ind on t(id);
set autotrace trace explain
select /*+ rule +*/ * from t where id = 99;
Note
-----
- rule based optimizer used (consider using cbo) select * from t where id=99;
select * from t where id=1;

2.验证动态采样
SQL>    set linesize 700
SQL>    set autotrace trace explain;
SQL>    select /*+ dynamic_sampling(t 0) +*/ * from t where id = 1;
//现在 T  表还没有被分析 hints/*+ dynamic_sampling(t0)+*/  是让 CBO  无法通过动态采样 获得表中实际数据的情况 此时,CBO  只能根据数据字典表 T  的非常有限的信息 (比如表 的 extents  数量,数据块的数量) 来猜测表中的数据
从结果中可以看到,CBO  猜出的 ID=1  的数据量只有 353  条,这个数值对于表的总和来说, 是一个比较小的值,所以 CBO  选择了索引而不是全表扫描。但是实际 ID  等于 1  的数据 有 8w  多,但是实际情况
SQL> select * from t where id = 1;
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true);
通过动态采样 (如果表没有做分析,Oracle   自动通过动态采样的方式来收集分析数据) , CBO 估算出表中的实际数据量 8w 多条 ( cardinality) ,从执行计划中 看到,这个数据值还 是相差不大,CBO  断定 ID=1  的数据基本上等同于表中的数据,所以选择了全表扫描。
SQL> select * from t where id = 99;
通过对表的分析,CBO  就可以获得 T  表和索引的充足的信息。上面的例子显示,CBO  从分 析数据中得到了 id=99  的数据记录是 1 ,所以选择了索引,这种情况下,索引的效率是 相 当高的。

3.分析表的意义
SQL> set autotrace off;
SQL> update t set id = 99;
SQL> commit;

SQL> set autotrace trace explain;
SQL> select * from t where id = 99;
然后我们把表中所有记录的 id  更新为 99 ,因为没有对表迚行分析,所以表中的分析数据 还是之前的信息,CBO  并不知道,可以看到,此时 rows 的值为 1 ,就是说,此时 CBO  仍 然认为表 T  中 id=99  的值只有 1  条,所以选择的仍然是索引。
SQL>    exec dbms_stats.gather_table_stats(USER,'T',cascade=>true);
重新对表迚行分析后,CBO  获得了正确的 rows 值 所以是全表扫描,这种情况下全表扫描 是最佳的执行计划。
SQL> select * from t where id = 99;

 4.多表查询 rows 的意义 

在多表关联查询或者是 SQL  中有子查询时,每个关联表或者是子查询的 rows 的值对主查 询的影响非常大,甚至可以说,CBO  就是依赖于各个关联表或者子查询 rows 值来计算出 最后的执行计划。
对于多表查询,CBO  使用每个关联表返回的行数决定使用什么样的访问方式,来做表关联 (比如 nestd loops join  或者是 hash join ) ;对于子查询,它的 rows 将决定 子查询是使用索 引还是使用全表扫描的方式访问数据。
下面通过完整的子句查询的例子。来关注在子查询中,rows 是如何影响主查询的执行计划

create table t1 (id int,name varchar2 (1000));
create table t2 (id int,name varchar2 (1000));
create index ind_t1 on t1 (id);
create index ind_t2 on t2 (id);
create index ind_t2_name on t2 (name);
insert into t1 select object_id,object_name from dba_objects;
commit;
exec
dbms_stats .gather_table_stats (user, 'T1 ',cascade=>true,method
_opt=> 'for all indexed columns ');

select * from t1 where id in (select /*+ dynamic_sampling (t2 0)
cardinality (t2 30000) +*/ id from t2 where name= 'AA ');
其中:
cardinality(t2 30000)的作用是告诉 CBO  从 t2  表将获得 30000  条数据。 dynamic_sampling(t2 0)的作用是禁止动态采样。
通过这种方式,模拟子查询中返回的结果数,同时为了让 CBO  完全依赖这个信息生成 执行计划,禁止子查询使用动态采样(dynamic_sampling  设置为 0)。
可以看到,当 CBO  得到来自于子查询中返回的结果集 ( row source ) 的记录数为 30000 条时候,采用了 hash join  的执行计划,hash join  通常适用于两张关联的表都比较大的时候。 下面我们将子查询的结果集变得很小
select * from t1 where id in (select /*+ dynamic_sampling (t2 0)
cardinality (t2 1) +*/ id from t2 where name= 'AA ');
将子查询的返回值设置为 1 ,即:cardinality(t2 1)
此时 CBO  选择了两表通过 nested loops join  迚行关联的执行计划,因为子查询只 有 1  条

记录,这时候 CBO  会选择最合适这种情况的 nested loop join  关联方式。 从这里我们得到这样的一个结论:
子查询的 rows 值直接影响了主查询的执行计划,如果 CBO  对子查询的 ROWS 判断 有误,那么主查询的执行计划很有可能是错误的。
在看多表查询时候,一定要注意每个操作 rows 值,如果这个数值明显不对,那么很可能   操作的表的分析数据出了问题或者表没有分析。比如上面的例子,你确切的知道 T2  表的数 据很小,而在执行计划显示却是 10000  条,这显然不对,这个时候就要去检查问题的 所在, 看看 T2  表是不是曾经有很多数据,删除之后没有做重做分析等。
 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

it技术分享just_free

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值