深入内核:CBO对于Cost值相同索引的选择

企业动态

 

[[163836]]

  崔华,网名 dbsnake

  Oracle ACE Director,ACOUG 核心专家

  编辑手记:感谢崔华授权我们独家转载其精品文章,也欢迎大家向“Oracle”社区投稿。

  这里我们稍微讨论一下CBO对于Cost值相同的索引的选择,可能会有朋友认为在同样Cost的情况下,Oracle会按照索引名的字母顺序来选择索引,实际上并不完全是这样,CBO对于Cost值相同的索引的选择和Oracle的版本有关。

  原理说明

  MOS上文章“Handling of equally ranked (RBO) or costed (CBO) indexes [ID 73167.1]”明确指出——When the CBO detects 2 indexes that cost the same, it makes the decision based on the following:

  (up to release 9.2.06) indexes ascii name so that index ‘AAA’ would be chosen over index ‘ZZZ’. See Bug 644757

  (starting with 9.2.0.7 and in 10gR1) bigger NDK for fully matched indexes (not for fast full scans). See Bug 2720661

  (in 10gR2 and above) index with lower number of leaf blocks. See Bug 6734618

  这意味着对于Oracle 10gR2及其以上的版本,CBO对于Cost值相同的索引的选择实际上会这样:

  1-如果Cost值相同的索引的叶子块数量不同,则Oracle会选择叶子块数量较少的那个索引;

  2-如果Cost值相同的索引的叶子块数量相同,则Oracle会选择索引名的字母顺序在前面的那个索引。

  测试验证

  这个非常容易验证,我们来看一个实例。在一个11.2.0.3的环境中创建一个测试表T1:

  Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.3.0

  Connected as nbs

  SQL> create table t1 as select * from dba_objects;

  Table created

  对T1增加一列object_id_1,并将其值修改成和列object_id的值一致:

  SQL> alter table t1 add (object_id_1 number);

  Table altered

  SQL> update t1 set object_id_1=object_id;

  83293 rows updated

  SQL> commit;

  Commit complete

  分别在列object_id和列object_id_1上创建名为a_idx_t1和b_idx_t1的B树索引:

  SQL> create index a_idx_t1 on t1(object_id);

  Index created

  SQL> create index b_idx_t1 on t1(object_id_1);

  Index created

  对表T1收集一下统计信息:

  SQL> exec dbms_stats.gather_table_stats(ownname => ‘NBS’, tabname => ‘T1’, estimate_percent => 100, cascade => TRUE, no_invalidate => false);

  PL/SQL procedure successfully completed

  此时索引a_idx_t1和b_idx_t1的统计信息显然是完全一致的(这意味着走这两个索引的同类型执行计划的Cost值会相同),从如下查询结果中我们可以看到,它们的叶子块的数量均为185:

  SQL> select index_name,leaf_blocks from dba_indexes where table_owner=’NBS’ and table_name=’T1′;

  INDEX_NAMELEAF_BLOCKS

  —————————— ———–

  A_IDX_T1185

  B_IDX_T1 185

  在当前情形下,如果我们执行目标SQL:

  “select * from t1 where object_id=1000 and object_id_1=1000”

  显然此时Oracle既可以走索引a_idx_t1,也可以走索引b_idx_t1。

  从如下查询结果中我们可以看到,此时Oracle选择了走索引a_idx_t1:

  SQL> set autotrace traceonly explain

 

  SQL> select * from t1 where object_id=1000 and object_id_1=1000;

  这就验证了我们之前提到的结论——对于Oracle 10gR2及其以上的版本,如果Cost值相同的索引的叶子块数量相同,则Oracle会选择索引名的字母顺序在前面的那个索引。

  现在我们把索引b_idx_t1的叶子块数量从之前的185改为现在的184:

  SQL> exec dbms_stats.set_index_stats(ownname => ‘NBS’, indname => ‘B_IDX_T1’, numlblks => 184);

  PL/SQL procedure successfully completed

  从如下查询结果中我们可以看到,上述改动生效了:

  SQL> select index_name,leaf_blocks from dba_indexes where table_owner=’NBS’ and table_name=’T1′;

  INDEX_NAMELEAF_BLOCKS

  —————————— ———–

  A_IDX_T1185

  B_IDX_T1184

  然后我们再次执行上述目标SQL:

 

  SQL> select * from t1 where object_id=1000 and object_id_1=1000;

  从上述显示内容中我们可以看到,上述SQL的执行计划从之前的走对索引a_idx_t1的索引范围扫描变为了现在的走对索引b_idx_t1的索引范围扫描,这就验证了我们之前提到的结论:对于Oracle 10gR2及其以上的版本,如果Cost值相同的索引的叶子块数量不同,则Oracle会选择叶子块数量较少的那个索引。

 

责任编辑:市场部 来源: IT专家网
相关推荐

2021-03-06 22:41:06

内核源码CAS

2020-11-12 18:08:05

JavaLinux多线程

2009-11-16 10:30:24

Oracle驱动表

2010-07-07 10:21:35

IGRP路由协议

2017-09-04 15:15:48

Linux内核内存屏障

2020-09-23 06:53:48

Linux内核架构

2009-07-02 09:25:15

选择Hibernate

2010-06-21 13:07:14

2009-07-06 16:08:00

ASP到JSP

2021-05-19 07:56:26

Linux内核抢占

2021-09-06 17:50:09

内核态操作系统

2009-12-24 17:06:35

编译Fedora内核

2018-09-04 16:20:46

MySQ索引数据结构

2023-11-24 11:24:16

Linux系统

2009-01-11 15:55:21

金蝶中间件易博讯Apusic

2023-01-10 08:50:10

OracleCBO优化器

2019-07-08 20:00:35

Linux内核模块

2020-11-20 07:55:55

Linux内核映射

2010-04-21 13:11:54

Unix内核

2017-11-20 11:05:23

数据库MongoDB索引
点赞
收藏

51CTO技术栈公众号