作者:kindle

一、索引的概念:

    数据库的索引类似于书籍的索引。在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库。

二、索引的特点:

    1.索引可以加快数据库的检索速度
    2.索引降低了数据库插入、修改、删除等维护任务的速度

三、oracle索引的类型:

b*树索引:

    这是Oracle中最常用的索引,它的构造类似于二叉树,能根据键提供一行或一个行集的快速访问,通常只需要很少的读操作就能找到正确的行。B*树索引由两列组成,第一列是ROWID, 它是行的位置(它指向了数据库表中对应的此行的位置);第二列是正被索引列的值(具体的被建立了索引的列的值,我们先是根据 

这个值通过导航块找到这个叶子节点的该项(值,rowid),然后通过rowid找到具体的这行记录)。

 

             图:典型的B*树索引布局

    这个树底层的块称为叶子节点/叶子块,其中分别包含各个索引键值以及一个rowid(它是指向所索引的行)。叶子节点之上的内部块称为分支块(branch block)/导航块,这些节点用于实现导航。例如,如果想在索引中找到值20,要从树顶开始,找到左分支,我们检查这个块,并发现需要找到范围"20..25"的块,这个块将是叶子块,其中会指示包含数20的行。索引的叶子节点实际上构成了一个双向链表。一旦发现要从叶子节点中的哪里开始,执行值的有序扫描就会很容易,我们就不必再在索引结构中导航,而只需根据叶子节点向前或向后扫描就可以了(比如要做between 20 to 40,我们只需要先导航找到20,然后顺序扫描直到遇到40即可)。

    B*树的特点之一是:所有叶子块都应该在树的同一层上,这一层称之为索引的高度, 它说明所有从索引的根块到叶子块的遍历都会访问同样数目的块。由此可见B*树的B代表的是balanced,所谓的"Height balanced"。大多数B*树索引的高度都是2或3,即使索引中有数百万行记录也是如此,这说明,一般而言,在索引中找到一个键只需要2到3次I/O , 这确实不错。

    B*树是一个极佳的通用索引机制,无论是大表还是小表都很适用,随着底层表大小增长,获取数据的性能仅会稍有恶化。

比如,我们为customers表建立一个常见的B*树索引: 

CREATE INDEX IDX_Cus_City on customers(city)

b*树子索引---复合索引

    复合索引也是一种B*树索引,它由多列组成。当我们拥有使用两列或超过两列的频繁查询时,就使用B*树复合索引,而其所使用的两列或多列在where子句中and逻辑操作符连接。因为复合索引中列的顺序很重要,所以确信以最有效的索引顺序排列他们,前导列应该是基数大的列(不同的取值多)。而且单独的where也会使用此索引,而后序列则必须与前导一起使用。

反向索引:

    Oracle推出它的主要目的就是为了降低在并行服务器(Oracle Parallel Server)环境下索引叶块的争用。当B*Tree索引中列是由递增的序列号产生的话,那么这些索引信息基本上分布在同一个叶块,当用户修改或访问相似的列时,索引块很容易产生争用。反向索引中的索引码将会被分布到各个索引块中,减少了争用。反向索引反转了索引码中每列的字节,使得它们不会分到一个叶子块中,但是反向索引也因此不能应用于范围搜索,因为它的相邻叶子块是无顺序的。

CREATE INDEX index_name on table_name(column_name) REVERSE ;

降序索引:

    降序索引是oracle 8i引入的,用以扩展B*树索引的功能,它允许在索引中以降序(从大到小的顺序)存储一列。对于降序的order by有效。

位图索引:

    当我们需要根据基数比较小的列进行查询时,一般使用位图索引会对查询效率有明显提高,如查询性别是女,已婚,汉族的人。(性别,婚否,民族基数都比较小)

CREATE BITMAP INDEX index_name ON table_name(column_name1,column_name2) TABLESPACE tablespace_name

函数索引:

    B*Tree的衍生产物,应用于查询语句条件列上包含函数的情况,索引中储存了经过函数计算的索引码值。可以在不修改应用程序的基础上能提高查询效率。

五、建立合适的索引:

1、最简单的情形:

   如果你在查询时常用类似以下的语句 + 表的记录比较多 + category_id基数比较大(不重复记录数) 
   SELECT * FROM mytable WHERE category_id=1;
   最直接的应对之道,是为category_id建立一个简单的索引:
  CREATE INDEX mytable_categoryid ON mytable (category_id);

2、如果你有不止一个选择条件,例如:
  SELECT * FROM mytable WHERE category_id=1 AND user_id=2;

   你可以建立复合索引。

   CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);

3、如果有个ORDER BY子句,那么我们就为这个ORDER BY字段建立一个索引

   如果是降序的,我们可以建立降序索引,否则建立普通索引。

4、在基数小的字段上要善于使用位图索引。

   如在员工信息表中的性别字段,一般就只有男跟女两个值,所以,其基数为2;婚姻状况字段的话,则其只有已婚、未婚、离婚三种状态,其基数就为3;民族一览内也是只有有限的几个值。

5、需要利用Join语句连接多个表时,把这些表格关联起来。为了提高数据库的查询效率,这些用来关联的字段,最好能够建立索引。这可以显著的提高查询的速度。

六、索引的屏蔽:

在SQL中有很多陷阱会使一些索引无法使用。下面讨论一些常见的问题:
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条件,就可以使用索引,以避免全表扫描。
2、 使用IS NULL 或IS NOT NULL

    使用IS NULL 或IS NOT NULL同样会限制索引的使用。因为NULL值并没有被定义。在SQL语句中使用NULL会有很多的麻烦。因此建议开发人员在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引)。
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、 比较不匹配的数据类型

    比较不匹配的数据类型也是比较难于发现的性能问题之一。注意下面查询的例子,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';