最近对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类型
运行脚本生成数据:
- declare
- cnt number := 0;
- begin
- execute immediate 'truncate table twovaluetab';
- for cnt in 0..20000 loop
- insert into twovaluetab values(1);
- end loop;
- insert into twovaluetab values(0);
- commit;
- end;
- /
执行语句:select * from twovaluetab where id = 1;来观察执行计划。在测试表中只有两个值0占据一行1占据20000行,根据理论,如果是CBO并且是获取全部数据值的话,那全表扫描在逻辑上将读取更少的数据块(如果是索引,那要读取索引块,再读取数据块,而全表则只要读取一次数据块)
先来看RULE(关于RULE具体是哪些,下篇文章会具体介绍)
然后我们来看ALL_ROWS,根据理论,应该是全表,不出意料:
然后是FISRT_ROWS,如果是最先的几行,那自然是index效率会比较高:
最后我们来测试choose:
- sys@ORA11G> exec dbms_stats.delete_table_stats(OWNNAME=>'sys',tabname=>'twovaluetab');
- PL/SQL procedure successfully completed.
首先保证,表没有统计数据,我们运行一个dbms_stats包(关于这个包的解释在之后的文章中会进行说明),把表的统计信息删除,根据理论,应该是和RBO结果一致:
然后我们收集一下表的统计信息,同样是使用dbms_stats包,
- sys@ORA11G> exec dbms_stats.gather_table_stats(OWNNAME=>'sys',tabname=>'twovaluetab',cascade=>true);
- 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已经有了一定的了解了,调优是十分考水平的一项工作,我们必须扎扎实实把原理弄弄明白,才能融汇贯通。