Oracle索引分析与比较

 索引在各种关系型数据库系统中都是举足轻重的组成部分,其对于提高检索数据的速度起至关重要的作用。在Oracle中,索引基本分为以下几种:B*Tree索引,反向索引,降序索引,位图索引,函数索引,interMedia全文索引等。本文主要就前6种索引进行分析,由于interMedia全文索引涉及的内容可以单独写一篇文章,所以不在此对其做分析。

    首先给出各种索引的简要解释:

    b*tree index:几乎所有的关系型数据库中都有b*tree类型索引,也是被最多使用的。其树结构与二叉树比较类似,根据rid快速定位所访问的行。
    反向索引:反转了b*tree索引码中的字节,是索引条目分配更均匀,多用于并行服务器环境下,用于减少索引叶的竞争。
    降序索引:8i中新出现的索引类型,针对逆向排序的查询。
    位图索引:使用位图来管理与数据行的对应关系,多用于OLAP系统。
    函数索引:这种索引中保存了数据列基于function返回的值,在select * from table where function(column)=value这种类型的语句中起作用。

    2 各种索引的结构分析

    2.1 B*Tree索引B*Tree索引是最常见的索引结构,默认建立的索引就是这种类型的索引。B*Tree索引在检索高基数数据列(高基数数据列是指该列有很多不同的值)时提供了最好的性能。当取出的行数占总行数比例较小时B-Tree索引比全表检索提供了更有效的方法。但当检查的范围超过表的10%时就不能提高取回数据的性能。B-Tree索引是基于二叉树的,由分支块(branch block)和叶块(leaf block)组成。在树结构中,位于最底层底块被称为叶块,包含每个被索引列的值和行所对应的rowid。在叶节点的上面是分支块,用来导航结构,包含了索引列(关键字)范围和另一索引块的地址。

    假设我们要找索引中值为80的行,从索引树的最上层入口开始,定位到大于等于50,然后往左找,找到第2个分支块,定位为75-100,最后再定位到叶块上,找到80所对应的rowid,然后根据rowid去读取数据块获取数据。如果查询条件是范围选择的,比如where column >20 and column <80,那么会先定位到第一个包含20的叶块,然后横向查找其他的叶块,直到找到包含80的块为止,不用每次都从入口进去再重新定位。

    2.2 反向索引

    反向索引是B*Tree索引的一个分支,它的设计是为了运用在某些特定的环境下的。Oracle推出它的主要目的就是为了降低在并行服务器(Oracle Parallel Server)环境下索引叶块的争用。当B*Tree索引中有一列是由递增的序列号产生的话,那么这些索引信息基本上分布在同一个叶块,当用户修改或访问相似的列时,索引块很容易产生争用。反向索引中的索引码将会被分布到各个索引块中,减少了争用。反向索引反转了索引码中每列的字节,通过dump()函数我们可以清楚得看见它做了什么。举个例子:1,2,3三个连续的数,用dump()函数看它们在Oracle内部的表示方法。

  
  
SQL > select ' number ' , dump ( 1 , 16 ) from dual 2 union all select ' number ' , dump ( 2 , 16 ) from dual 3 union all select ' number ' , dump ( 3 , 16 ) from dual; ' NUMBE DUMP(1,16) ------ ----------------- number Typ=2 Len=2: c1,2 (1) number Typ=2 Len=2: c1,3 (2) number Typ=2 Len=2: c1,4 (3) 再对比一下反向以后的情况: SQL> select ' number ' ,dump(reverse(1),16) from dual 2 union all select ' number ' ,dump(reverse(2),16) from dual 3 union all select ' number ' ,dump(reverse(3),16) from dual; ' NUMBE DUMP ( REVERSE ( 1 ), 1 -- ---- ----------------- number Typ = 2 Len = 2 : 2 ,c1 ( 1 number Typ = 2 Len = 2 : 3 ,c1 ( 2 number Typ = 2 Len = 2 : 4 ,c1 ( 3

    我们发现索引码的结构整个颠倒过来了,这样1,2,3个索引码基本上不会出现在同一个叶块里,所以减少了争用。不过反向索引又一个缺点就是不能在所有使用常规索引的地方使用。在范围搜索中其不能被使用,例如,where column>value,因为在索引的叶块中索引码没有分类,所以不能通过搜索相邻叶块完成区域扫描。

    2.3 降序索引

    降序索引是8i里面新出现的一种索引,是B*Tree的另一个衍生物,它的变化就是列在索引中的储存方式从升序变成了降序,在某些场合下降序索引将会起作用。举个例子,我们来查询一张表并进行排序:

  
  
SQL > select * from test where a between 1 and 100 order by a desc ,b asc ; 已选择100行。 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 2 Card = 100 Bytes = 400 ) 1 0 SORT( ORDER BY )(Cost = 2 Card = 100 Bytes = 400 ) 2 1 INDEX (RANGE SCAN) OF ' IND_BT ' (NON - UNIQUE ) (Cost = 2 Card = 100 Bytes = 400 )

    这里优化器首先选择了一个索引范围扫描,然后还有一个排序的步骤。如果使用了降序索引,排序的过程会被取消。

  
  
SQL > create index test.ind_desc on test.testrev(a desc ,b asc ); 索引已创建。 SQL > analyze index test.ind_desc compute statistics ; 索引已分析

    再来看下执行路径:

  
  
SQL > select * from test where a between 1 and 100 order by a desc ,b asc ; 已选择100行。 Execution Plan (SQL执行计划,稍后会讲解如何使用)。 -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 2 Card = 100 Bytes = 400 ) 1 0 INDEX (RANGE SCAN) OF ' IND_DESC ' (NON - UNIQUE ) (Cost = 2 Card = 100 Bytes = 400 )

    我们看到排序过程消失了,这是因为创建降序索引时Oracle已经把数据都按降序排好了。另外一个需要注意的地方是要设置init.ora里面的compatible参数为8.1.0或以上,否则创建时desc关键字将被忽略。

(2)条件列包含函数但没有创建函数索引。
   
   
SQL > select /*+ RULE */ * FROM test.testindex where upper (a) = ' A ' ;(使用了函数upper()在列a上); A B -- ---------- a 2 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = HINT: RULE 1 0 TABLE ACCESS ( FULL ) OF ' TESTINDEX ' (优化器选择全表扫描) -- -------------------------------------------------------- 创建基于函数的索引 SQL > create index test.ind_fun on test.testindex( upper (a)); 索引已创建。 SQL > insert into testindex values ( ' a ' , 2 ); 已创建1行。 SQL > commit ; 提交完成。 SQL > select /*+ RULE*/ * FROM test.testindex where upper (a) = ' A ' ; A B -- ---------- a 2 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = HINT: RULE 1 0 TABLE ACCESS ( FULL ) OF ' TESTINDEX ' (在RULE优化器下忽略了函数索引选择了全表扫描) -- --------------------------------------------------------- SQL > select * FROM test.testindex where upper (a) = ' A ' ; A B -- ---------- a 2 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 2 Card = 1 Bytes = 5 ) 1 0 TABLE ACCESS ( BY INDEX ROWID) OF ' TESTINDEX ' (Cost = 2 Card = 1 Bytes = 5 ) 2 1 INDEX (RANGE SCAN) OF ' IND_FUN ' (NON - UNIQUE ) (Cost = 1 Car d = 1 )(CBO优化器使用了ind_fun索引)

      (3)复合索引中的前导列没有被作为查询条件。

   
   
创建一个复合索引 SQL > create index ind_com on test.testindex(a,b); 索引已创建。 SQL > select /*+ RULE*/ * from test.testindex where a = ' 1 ' ; A B -- ---------- 1 2 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = HINT: RULE 1 0 INDEX (RANGE SCAN) OF ' IND_COM ' (NON - UNIQUE )(条件列表包含前导列时使用索引ind_com) SQL > select /*+ RULE*/ * from test.testindex where b = 1 ; 未选定行 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = HINT: RULE 1 0 TABLE ACCESS ( FULL ) OF ' TESTINDEX ' (条件列表不包括前导列是选择全表扫描) -- ---------------------------------------------------------

     (4)CBO模式下选择的行数比例过大,优化器采取了全表扫描。

   
   
SQL > select * from test.testindex where a = ' 1 ' ; A B -- ---------- 1 2 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 1 Card = 1 Bytes = 5 ) 1 0 TABLE ACCESS ( FULL ) OF ' TESTINDEX ' (Cost = 1 Card = 1 Bytes = 5 ) (表一共2行,选择比例为50 % ,所以优化器选择了全表扫描) ―――――――――――――――――――――――――――――――――― 下面增加表行数 SQL > declare i number ; 2 begin 3 for i in 1 .. 100 loop 4 insert into test.testindex values (to_char(i),i); 5 end loop; 6 end ; 7 / PL / SQL 过程已成功完成。 SQL > commit ; 提交完成。 SQL > select count ( * ) from test.testindex; COUNT ( * ) -- -------- 102 SQL > select * from test.testindex where a = ' 1 ' ; A B -- -- ---------- 1 1 1 2 Execution Plan SELECT STATEMENT Optimizer = CHOOSE (Cost = 1 Card = 1 Bytes = 5 ) 1 0 INDEX (RANGE SCAN) OF ' IND_COM ' (NON - UNIQUE ) (Cost = 1 Card = 1 Bytes = 5 ) (表一共102行,选择比例为2 / 102 = 2 % ,所以优化器选择了索引扫描)
(5)CBO模式下表很久没分析,表的增长明显,优化器采取了全表扫描。
   
   
SQL > select * from test.testindex where a like ' 1% ' ; A B -- -- ---------- 1 2 1 1 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 100 100 已选择13行。 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 1 Card = 13 Bytes = 52 ) 1 0 TABLE ACCESS ( FULL ) OF ' TESTINDEX ' (Cost = 1 Card = 13 Bytes = 52 ) (表一共102行,选择比例为13 / 102 > 10 % ,优化器选择了全表扫描) ―――――――――――――――――――――――――――――――――― 增加表行数 SQL > declare i number ; 2 begin 3 for i in 200 .. 1000 loop 4 insert into test.testindex values (to_char(i),i); 5 end loop; 6 end ; 7 / PL / SQL 过程已成功完成。 SQL > commit ; 提交完成。 SQL > select count ( * ) from test.testindex; COUNT ( * ) -- -------- 903 SQL > select * from test.testindex where a like ' 1% ' ; A B -- -- ---------- 1 2 1 1 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 100 100 1000 1000 已选择14行。 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 1 Card = 13 Bytes = 52 ) 1 0 TABLE ACCESS ( FULL ) OF ' TESTINDEX ' (Cost = 1 Card = 13 Bytes = 52 ) (表一共903行,选择比例为14 / 903 < 5 % ,优化器选择了全表扫描,选择路径是错误的) ――――――――――――――――――――――――――――― 给表做分析 SQL > analyze table test.testindex compute statistics for table for all indexed c olumns for all indexes; 表已分析。 SQL > select * from test.testindex where a like ' 1% ' ; A B -- -- ---------- 1 2 1 1 10 10 100 100 1000 1000 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 已选择14行。 Execution Plan -- -------------------------------------------------------- 0 SELECT STATEMENT Optimizer = CHOOSE (Cost = 4 Card = 24 Bytes = 120 ) 1 0 TABLE ACCESS ( BY INDEX ROWID) OF ' TESTINDEX ' (Cost = 4 Card = 24 Bytes = 120 ) 2 1 INDEX (RANGE SCAN) OF ' IND_COLA ' (NON - UNIQUE ) (Cost = 2 Ca rd = 24 ) (经过分析后优化器选择了正确的路径,使用了ind_cola索引) ――――――――――――――――――――――――――――――――――

    小结

    这篇文章介绍了oracle数据库中的各种索引,通过分析它们的结构,我们知道了它们的特点和应用范围,同时介绍了如何去避免索引失效,希望对大家有所帮助。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值