返璞归真 - 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执行计划案例的主要内容,如果未能解决你的问题,请参考以下文章