欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

oracle 中的优化 - RBO 和 CBO 简介以及优化器模式参数说明

最编程 2024-03-28 18:45:28
...

最近对oracle的优化比较感兴趣,所以想跟大家分享一下学习经验。

在oracle中,sql语句优化分成RBO(Rule-Based Optimization)基于规则的优化和CBO(Cost-Based Optimization)基于代价的优化。

在较早的oracle的版本中,oracle是采取基于规则的优化,根据oracle指定的规则优先顺序,对于指定的表进行执行计划的选择。比如在规则中,索引的优先级大于全表扫描,那在查询某张拥有所有的表的时候,那就一定是使用索引。

在后来,oracle发现这样的做法并不科学,规则是死的,数据是活的,并不是说在所有的情况下使用规则都是可行的。于是oracle开始推出了基于代价的优化,收集对象的统计信息并分析得出最优的执行计划。从oracle9i开始,oracle就强烈建议使用CBO,这点从oracle10g和oracle11g的文档中关于优化参数optimizer_mode完全不提与RBO有关的几个选项就可以看出。

(有人说CBO考虑的代价主要是cpu和内存,这点o小白不太赞同,在sql执行中,我们最需要考虑的一定是和io有关的部分,所以考虑的方向应该是逻辑读。在下面的一个测试例子中可以看出RBO和CBO的一些区别)

optimizer_mode决定了oracle使用RBO还是CBO,可选值如下:

Choose:这是RBO和CBO“中间”的一种模式,具体是这样:

1.当所包含的所有对象有统计信息时,那就是用CBO的优化方式。

2.当所包含的部分对象有统计信息是,也使用CBO的方式,并且对剩下的对象的统计信息进行“猜测”。

3.如果包含所有对象都没有统计信息时,使用RBO的方式。

这个参数在oracle9i中是默认值,但是从oracle10g版本,默认值变为ALL_ROWS,并且文档中完全不提到这个参数(还有另一个参数是RULE)也就是说oracle倾向于完全不用RBO(Hint在之后的文章中讨论)。

RULE:oracle采用RBO的方式工作。

ALL_ROWS:oracle采取CBO的方式工作,并且计算代价是获取所有行数的代价,也就是如何消耗最少的资源完成整个查询。

FISRT_ROWS_n(n可以取值的有1,10,100,1000):oracle采取CBO的方式工作,计算代价是获取前n行的代价,也就是如何消耗最少的资源完成前n行的查询。oracle想的很周到,如果有top n的需求,那用这个参数来设置是最好不过的了。

FISRT_ROWS:和上面的参数相同,只是为了向后兼容。

那我们来做个简单的实验看看几个参数有什么不同。(为了测试方便,我们使用pl/sql developer来调整sql的优化模式):

实验表,twovaluetab 字段 id number类型

运行脚本生成数据:

  1. declare 
  2. cnt number := 0; 
  3. begin 
  4.         execute immediate 'truncate table twovaluetab'
  5.         for cnt in 0..20000 loop 
  6.         insert into twovaluetab values(1); 
  7.         end loop; 
  8.         insert into twovaluetab values(0); 
  9.         commit
  10. end

执行语句:select * from twovaluetab where id = 1;来观察执行计划。在测试表中只有两个值0占据一行1占据20000行,根据理论,如果是CBO并且是获取全部数据值的话,那全表扫描在逻辑上将读取更少的数据块(如果是索引,那要读取索引块,再读取数据块,而全表则只要读取一次数据块)

先来看RULE(关于RULE具体是哪些,下篇文章会具体介绍)

然后我们来看ALL_ROWS,根据理论,应该是全表,不出意料:

然后是FISRT_ROWS,如果是最先的几行,那自然是index效率会比较高:

最后我们来测试choose:

  1. sys@ORA11G> exec dbms_stats.delete_table_stats(OWNNAME=>'sys',tabname=>'twovaluetab'); 
  2.  
  3. PL/SQL procedure successfully completed. 

首先保证,表没有统计数据,我们运行一个dbms_stats包(关于这个包的解释在之后的文章中会进行说明),把表的统计信息删除,根据理论,应该是和RBO结果一致:

然后我们收集一下表的统计信息,同样是使用dbms_stats包,

  1. sys@ORA11G> exec dbms_stats.gather_table_stats(OWNNAME=>'sys',tabname=>'twovaluetab',cascade=>true); 
  2.  
  3. PL/SQL procedure successfully completed. 

再看结果,应该是和ALL_ROWS的CBO是一致的。

最后是optimizer_mode这个参数既可以在session级临时调整也可以在在system级调整。

alter session set optimizer_mode=choose;

alter system set optimizer_mode=all_rows scope = spfile;

理论配合简单实验,相信大家对RBO和CBO以及参数optimizer_mode已经有了一定的了解了,调优是十分考水平的一项工作,我们必须扎扎实实把原理弄弄明白,才能融汇贯通。