oracle索引原理

一、索引原理

    Oracle提供了大量索引选项。知道在给定条件下使用哪个选项对于一个应用程序的性能来说非常重要。一个错误的选择可能会引发死锁,并导致数据库性能急剧下降或进程终止。而如果做出正确的选择,则可以合理使用资源,使那些已经运行了几个小时甚至几天的进程在几分钟得以完成,这样会使您立刻成为一位英雄。这篇文章就将简单的讨论每个索引选项。主要有以下内容:


[1] 基本的索引概念
    查询DBA_INDEXES视图可得到表中所有索引的列表,注意只能通过USER_INDEXES的方法来检索模式(schema)的索引。访问USER_IND_COLUMNS视图可得到一个给定表中被索引的特定列。


[2] 组合索引
    当某个索引包含有多个已索引的列时,称这个索引为组合(concatented)索引。在 Oracle9i引入跳跃式扫描的索引访问方法之前,查询只能在有限条件下使用该索引。比如:表emp有一个组合索引键,该索引包含了empno、 ename和deptno。在Oracle9i之前除非在where之句中对第一列(empno)指定一个值,否则就不能使用这个索引键进行一次范围扫描。
特别注意:在Oracle9i之前,只有在使用到索引的前导索引时才可以使用组合索引!


[3] oracle ROWID
    通过每个行的ROWID,索引Oracle提供了访问单行数据的能力。ROWID其实就是直接指向单独行的线路图。如果想检查重复值或是其他对ROWID本身的引用,可以在任何表中使用和指定rowid列。


[4] 限制索引
    限制索引是一些没有经验的开发人员经常犯的错误之一。在SQL中有很多陷阱会使一些索引无法使用。下面讨论一些常见的问题:

4.1 使用不等于操作符(<>、!=)
    下面的查询即使在cust_rating列有一个索引,查询语句仍然执行一次全表扫描。
         select cust_Id,cust_name
         from   customers
         where  cust_rating <> 'aa';
     把上面的语句改成如下的查询语句,这样,在采用基于规则的优化器而不是基于代价的优化器(更智能)时,将会使用索引。
         select cust_Id,cust_name
         from   customers
         where  cust_rating < 'aa' or cust_rating > 'aa';
    特别注意:通过把不等于操作符改成OR条件,就可以使用索引,以避免全表扫描。
    

4.2 使用IS NULL 或IS NOT NULL
    使用IS NULL 或IS NOT NULL同样会限制索引的使用。因为NULL值并没有被定义。在SQL语句中使用NULL会有很多的麻烦。因此建议开发人员在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引,关于位图索引在稍后在详细讨论)。
4.3 使用函数
如果不使用基于函数的索引,那么在SQL语句的WHERE子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。 下面的查询不会使用索引(只要它不是基于函数的索引)

          select empno,ename,deptno

          from   emp

          where  trunc(hiredate)='01-MAY-81';

          把上面的语句改成下面的语句,这样就可以通过索引进行查找。

          select empno,ename,deptno

          from   emp

          where  hiredate<(to_date('01-MAY-81')+0.9999);



     4.4 比较不匹配的数据类型

         比较不匹配的数据类型也是比较难于发现的性能问题之一。

         注意下面查询的例子,account_number是一个VARCHAR2类型,

         在account_number字段上有索引。下面的语句将执行全表扫描。

         select bank_name,address,city,state,zip

         from   banks

         where  account_number = 990354;

         Oracle可以自动把where子句变成to_number(account_number)=990354,这样就限制了

          索引的使用,改成下面的查询就可以使用索引:

         select bank_name,address,city,state,zip

         from   banks

         where  account_number ='990354';

     特别注意:不匹配的数据类型之间比较会让Oracle自动限制索引的使用,

        即便对这个查询执行Explain Plan也不能让您明白为什么做了一次“全表扫描”。
 

[5] 选择性
    使用USER_INDEXES视图,该视图中显示了一个distinct_keys列。比较一下唯一键的数量和表中的行数,就可以判断索引的选择性。选择性越高,索引返回的数据就越少。


[6] 群集因子(Clustering Factor)
    Clustering Factor位于USER_INDEXES视图中。该列反映了数据相对于已索引的列是否显得有序。如果Clustering Factor列的值接近于索引中的树叶块(leaf block)的数目,表中的数据就越有序。如果它的值接近于表中的行数,则表中的数据就不是很有序。


[7] 二元高度(Binary height)
    索引的二元高度对把ROWID返回给用户进程时所要求的I/O量起到关键作用。在对一个索引进行分析后,可以通过查询DBA_INDEXES的B- level列查看它的二元高度。二元高度主要随着表的大小以及被索引的列中值的范围的狭窄程度而变化。索引上如果有大量被删除的行,它的二元高度也会增加。更新索引列也类似于删除操作,因为它增加了已删除键的数目。重建索引可能会降低二元高度。


[8] 快速全局扫描
    在Oracle7.3后就可以使用快速全局扫描(Fast Full Scan)这个选项。这个选项允许Oracle执行一个全局索引扫描操作。快速全局扫描读取B-树索引上所有树叶块。初始化文件中的 DB_FILE_MULTIBLOCK_READ_COUNT参数可以控制同时被读取的块的数目。


[9] 跳跃式扫描
    从Oracle9i开始,索引跳跃式扫描特性可以允许优化器使用组合索引,即便索引的前导列没有出现在WHERE子句中。索引跳跃式扫描比全索引扫描要快的多。下面的程序清单显示出性能的差别:

    create index skip1 on emp5(job,empno);

    index created.



    select count(*)

    from emp5

    where empno=7900;



    Elapsed:00:00:03.13



    Execution Plan

    0     SELECT STATEMENT Optimizer=CHOOSE(Cost=4 Card=1 Bytes=5)

    1  0    SORT(AGGREGATE)

    2  1      INDEX(FAST FULL SCAN) OF 'SKIP1'(NON-UNIQUE)



    Statistics



    6826 consistent gets

    6819 physical   reads



    select /*+ index(emp5 skip1)*/ count(*)

    from emp5

    where empno=7900;



    Elapsed:00:00:00.56



    Execution Plan

    0     SELECT STATEMENT Optimizer=CHOOSE(Cost=6 Card=1 Bytes=5)

    1  0    SORT(AGGREGATE)

    2  1      INDEX(SKIP SCAN) OF 'SKIP1'(NON-UNIQUE)



    Statistics



    21 consistent gets

    17 physical   reads
[10] 索引的类型

     B-树索引

     位图索引

     HASH索引

     索引编排表

     反转键索引

     基于函数的索引

     分区索引

     本地和全局索引

 

二、使用ORACLE索引时的技巧

索引对数据库访问的性能的作用十分巨大,设计合理的索引对于系统性能调整至关重要。而使用索引又是数据库开发过程   中最困难的一点。在设计索引的时候要注意以下几个方面:

[1]在OLTP系统中,尽量避免全表扫描,尽量使绝大多数操作都通过索引访问

[2]数据量很大并且经常变动的表上的索引不易过多,过量的索引会导致插入、更新和删除操作变慢,产生大量的IO,如果一张表上的索引超过8个,就需要检查是否这些索引都是必要的。(但是要值得注意的是这条原则很可能被过度的夸大。因为绝大多数OLTP系统,写操作不足10%,绝大多数的操作是读。因此如果是设计的合理,并不能说一张表上的索引超过多少就是不合理的)

[3]如果索引数量过多,建议删除部分所有的列都建有独立索引的复合索引,复合查询操作可以通过使用两个独立列的索引结果集合并来获取,也可以保障查询效率

[4]小表不要建立索引,可能通过索引访问速度更慢,把小表放入KEEP 池效率更高

[5]索引的PCTFREE、INITTRANS、MAXTRANS参数设置十分重要,特别是对于变化十分大的索引

[6]对于比较大的索引,使用索引分区会改善效率

[7]位图索引对于列表类值的效果较好(索引大小也比较小),但是位图索引不适合变化十分频繁的表

[8]使用函数索引可以避免大量不必要的全表扫描

[9]如果索引包含了查询需要的所有信息,查询就不需要访问表的数据,可以大大提高访问效率,因此要注意建立合理的复合索引,并注意SQL语句的合理性

[10]用好索引组织表

[11]可以使用反转索键值索引来消除索引相关的热块

[12]随着数据的变化,索引的效率会下降,因此定期重建索引对于性能提升有很大帮助

[13]使用CBO优化器的情况下,保证表和索引的数据得到了良好的分析是保证优化器选择最佳执行计划的关键

[14]删除所有不必要的索引。

 

三、具体例子

当WHERE条件中的字段是类似column1 is null 或者column1 is not null是,即便column1上面本来有索引也不会用到

比如where column1 =‘aaa’ 是可以用到索引的

下面的情况都用不到索引
column1||column2=‘aaabbb’
Substr(column1,1)=‘aaa’
Column1||’b’ =‘aaab’
Column3 + 1 > :a

Like 的字符串中第一个字符如果是‘%’则用不到索引
Column1 like ‘aaa%’ 是可以的
Column1 like ‘%aaa%’用不到

<>也用不到索引
有时可以采取适当措施改写后可以用到索引
例:column1 有3个值(‘A’,’B’,’C’),三个值的分布为‘A’ 10%, ‘B’ 80%,’C’ 10%,则column1 <> ‘B’ 可以改写为column1 in(‘A’,’C’)

尽管In写法要比exists简单一些,exists一般来说性能要比In要高的多
用In还是用Exists的时机
当in的集合比较小的时候,或者用Exists无法用到选择性高的索引的时候,用In要好,否则就要用Exists
例:select count(*) from person_info where xb in (select xb_id from dic_sex);
Select count(*) from n_acntbasic a where shbxdjm =:a and exists(select 1 from person_info where pid=a.pid and …);

Select * from person_info where zjhm=3101….;将会对person_info全表扫描
Select * from person_info where zjhm =‘3101…’才能用到索引

假定TEST表的dt字段是date类型的并且对dt建了索引。
如果要查‘20041010’一天的数据.下面的方法用不到索引
Select * from test where to_char(dt,’yyyymmdd’) =‘20041010’;
而select * from test where dt >=to_date(‘20041010’,’yyyymmdd’) and dt < to_date(‘20041010’,’yyyymmdd’) + 1 将会用到索引。

如果能不用到排序,则尽量避免排序。
用到排序的情况有
集合操作。Union ,minus ,intersect等,注:union all 是不排序的。
Order by
Group by
Distinct
In 有时候也会用到排序
确实要排序的时候也尽量要排序小数据量
,尽量让排序在内存中执行,有文章说,内存排序的速度是硬盘排序的1万倍。

你可能感兴趣的:(oracle知识点)