返璞归真 - Oracle 9i的RBO执行计划案例

Posted bisal的个人杂货铺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了返璞归真 - Oracle 9i的RBO执行计划案例相关的知识,希望对你有一定的参考价值。

点击标题下「蓝色微信名」可快速关注

预计阅读时间:15分钟


最近配合DBA团队,做了一次Oracle 9i老旧应用的搬迁工作,偶然发现应用某一个逻辑,执行缓慢,进而根据代码,找到对应的SQL语句,分析过程,还是有些意思,也暴露了些问题。


考虑到信息安全,我们通过创建模拟表,进行问题的说明,

1. 这是一张普通的堆表,表的数据量大约有1000万,业务数据按照日期存储,即日期类型字段c,存储了180天的数据。

2. 有两个索引,一个是含(a,b,c,d,e)五个字段的复合索引,一个是日期类型字段c的单键值索引。

create table tbl(
id number primary key,
a varchar2(1),
b varchar2(1),
c date,
d varchar2(1),
e varchar2(1));

create index idx_01 on tbl(a, b, c, d, e);
create index idx_02 on tbl(c);


执行的SQL语句如下,

select a, b from tbl 
where a=upper('A') and b=upper('B') and c=upper('01JAN10')
group by d, e
order by e;


他的执行计划为,注意“rule based optimization”,表示用了RBO,

SQL> explain plan for select a, b from tbl 
where a=upper('A') and b=upper('B') and c=upper('01JAN10')
group by d, e
order by e;
Explained.

SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------

----------------------------------------------------------------------------------
| Id  | Operation              |  Name    | Rows | Bytes | Cost |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |         |     |    |    |
|   1 |  SORT GROUP BY          |         |     |    |    |
|*  2 |   TABLE ACCESS BY INDEX ROWID| TBL      |     |    |    |
|*  3 |    INDEX RANGE SCAN      | IDX_02    |     |    |    |
----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  2 - filter("A"='A' AND "B"='B')
  3 - access("C"='01JAN10')

Note: rule based optimization


上面说了,这张表有两个索引,一个是含(a,b,c,d,e)五个字段的复合索引,一个是日期类型字段c的单键值索引,这条SQL语句的检索条件,用了复合索引的前三个字段,相对于日期类型字段c,感觉前者更高效,但实际用了c单键值索引。


这其实和9i的特性有关,在Oracle 9i下,默认的数据库优化器类型,optimizer_mode参数值是CHOOSE,

SQL> show parameter optimizer_mode

NAME              TYPE     VALUE
---------------------- ----------- ------------
optimizer_mode       string    CHOOSE


CHOOSE是Oracle 9i的默认值。采用这个值时,Oracle即可以采用基于规则RBO,也可以采用基于代价的CBO,到底使用那个值,取决于当前SQL的被访问的表中是不是有可以使用的统计信息。


如果有多个被访问的表,其中有一个或多个有统计信息,那么Oralce会对没有统计信息的表进行采样统计(即不全部采样),统计完成后,使用基于代价的优化方法CBO。


如果所有被访问的表都没有统计信息,Oracle就会采用基于规则的优化方法RBO。


表TBL,的确没有任何统计信息,

SQL> select table_name, num_rows, last_analyzed from user_tables where table_name='TBL';

TABLE_NAME  NUM_ROWS  LAST_ANAL
----------- ---------- ---------
TBL

SQL> select table_name, index_name, num_rows, last_analyzed from user_indexes
   where table_name='TBL';

TABLE_NAME    INDEX_NAME   NUM_ROWS  LAST_ANAL
-------------- ------------- ---------- ---------
TBL         IDX_01
TBL         IDX_02


因此选择的优化器是RBO,R表示的是Rule,即按照规则,来确定执行计划的效率,相当于硬编码,如下是1-15,15个等级的访问路径权重,等级越低,Oracle则认为效率越高,由此可见,效率最高的是根据rowid检索,关于rowid的知识,可以参考《》,效率最高的是全表扫描,我们注意到,复合索引的排名,要高于单键值索引效率,但为何此处未选择复合索引?


针对这问题,也请教了白鳝和惜纷飞老师,实际情况更复杂,不是仅仅简单的靠规则,还有SQL写顺序等,并且再仔细看官方文档,是这么说,

This access path is available if the statement's WHERE clause uses all columns of a composite index in equality conditions combined with AND operators . To execute the statement, Oracle performs a range scan on the index to retrieve rowids of the selected rows, and then accesses the table by those rowids.


注意红色字体,强调了在SQL语句中,用AND连接的“=”等值检索条件,必须是这个复合索引中,所有的字段。


但实际这条SQL,检索条件,只是复合索引的前三个字段,因此不符合Rule规则,即不认为此处要用复合索引,我们进一步看,如果检索条件,是复合索引所有的字段,是否会使用复合索引?如下测试,显示的确会用,RBO认为,IDX_01这个复合索引,效率要高于IDX_02这个单键值索引,

SQL> explain plan for select a, b from tbl 
where a=upper('A') and b=upper('B') and c=upper('01JAN10')
and d=upper('D') and e=upper('E')
group by d, e
order by e;
Explained.

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------
| Id  | Operation        |  Name  | Rows | Bytes | Cost |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |    |     |    |
|   1 |  SORT GROUP BY    |       |    |     |    |
|*  2 |   INDEX RANGE SCAN | IDX_01  |    |     |    |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  2 - access("A"='CA' AND "B"='B' AND
             "C"='01JAN10' AND "D"='D' AND
             "E"='E')

Note: rule based optimization


如果我们采集了统计信息,

SQL> exec dbms_stats.gather_table_stats('USER','TBL',cascade=>true);


再次执行SQL,此时会用CBO,根据成本值估算,发现已经用了IDX_01复合索引,认为复合索引的效率,高于单键值索引,

SQL> explain plan for select a, b from tbl 
where a=upper('A') and b=upper('B') and c=upper('01JAN10')
group by d, e
order by e;
Explained.

SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------

----------------------------------------------------------------------------------
| Id  | Operation           | Name   | Rows | Bytes | Cost |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |    |     |    |
|   1 |  SORT GROUP BY NOSORT  |      |  20 |  660 |   2|
|*  2 |   INDEX RANGE SCAN    | IDX_01 |   20 |  660 |   2|
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  2 - access("A"='CA' AND "B"='B' AND
             "C"='01JAN10' AND "D"='D' AND
             "E"='E')

Note: cpu costing is off


注意这有一句“cpu costing is off”,他的意思是未考虑CPU成本,是否考虑CPU成本,取决于系统统计信息,是否被采集了,可以用如下语句,

select pname, pval1 from sys.aux_stats$ 
where sname='SYSSTSTS_MAIN';


如果为空,则表示未采集,可以执行dbms_stats.gather_system_stats,采集一下,此时就会考虑CPU成本值。


还有个知识点,我们之前了解了10053,可以得出Oracle选择执行计划的依据,

《》

《》

《》

但是只有使用CBO优化器的时候,才可以产生10053的trace,否则使用RBO,trace文件中只会包含SQL语句,没有其他信息。


另外,在处理过程中,亲身体会到什么是定势思维,

思维定势(Thinking Set),也称“惯性思维”,是由先前的活动而造成的一种对活动的特殊的心理准备状态,或活动的倾向性。在环境不变的条件下,定势使人能够应用已掌握的方法迅速解决问题。而在情境发生变化时,它则会妨碍人采用新的方法。消极的思维定势是束缚创造性思维的枷锁。


采集完统计信息,一开始看见这条语句,执行计划使用了复合索引,有些迷惑,这里明明字段用了函数,怎么还能用索引?甚至还问了eygle,现在来看很幼稚,实在丢人,一开始看见了upper(),就习惯性认为,普通索引会失效,但upper()作用于右值,并不是左值,并不会影响索引的使用,

select a, b from tbl 
where a=upper('A') and b=upper('B') and c=upper('01JAN10')
group by d, e
order by e;


secooler说的很中肯,“如果深入原理的话,就不会有这种思维定式了”,的确,待提高的地方还很多,要培养正确的思维定式,“没有幼稚的问题,思考是最重要的品质”,又激起了学习的欲望。




以上是关于返璞归真 - Oracle 9i的RBO执行计划案例的主要内容,如果未能解决你的问题,请参考以下文章

[转帖]CBO和RBO

Oracle_sql优化基础——优化器总结

Oracle优化器

对Cost (%CPU) 粗略的理解

Oracle 优化器

总有一种SQL执行计划绑定方式合适你