Oracle索引原理

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索引
索引编排表
反转键索引
基于函数的索引
分区索引
本地和全局索引

热点文章

[@more@]
计算索引的大小
从8.0开始,ORACLE把 num_rows 加入 dba_indexes 表. 之前,我们需要将dba_indexes 和 dba_tables 做一个联合查询才能得到一个索引的num_rows 。在执行这个操作时,我们没有考虑到索引值可能为空而没有被包括在内的情况。索引的行数有可能会小于或等于表的行数。因此,我们所添加的区域所占用的空间大小就最终决定了索引的空间大小。

运行下边的语句,可以查看当前表TASK的索引用到的空间:

select a.owner,a.index_name,a.leaf_blocks,a.num_rows
from dba_indexes a
where a.owner not in ('SYS','SYSTEM')
and a.index_type!='BITMAP'
and a.last_analyzed is not null
and a.table_name = 'TASK';

OWNER INDEX_NAME LEAF_BLOCKS NUM_ROWS
ORADBA TASK_APPT_FK 65 23771
ORADBA TASK_CENTER_FK 99 45975
ORADBA TASK_CLIENT_FK 101 45975
ORADBA TASK_ENC_FK 117 45975
ORADBA TASK_TYPE_FK 74 45975
ORADBA TASK_DT_REQ 84 45975

看这里,我们查出当前索引 task_appt_ft 的23771行 都在65 个块中,可能有不用的空间在这些 65个块中,但是我们稍后将会处理。这意味着,我们在每个块中大约拥有366行 (23,771/65) 。使用在上一章--B-Tree 大小中我们提到的逻辑,如果我们有超过 366个块,那么我们会有一个branch blocks。 但是这个索引只有这个root block。而索引的真正需求是65 leaf_blocks+1 root block =66个块,正像它目前被定义的一样。

那么索引到底需要多大空间呢?我们可以从dba_segments 找到答案,让我们把它加入到我们的查询中;

select a.owner,a.index_name,a.leaf_blocks,a.num_rows,b.blocks
from dba_indexes a
,dba_segments b
where a.owner not in ('SYS','SYSTEM')
and a.index_type!='BITMAP'
and a.last_analyzed is not null
and a.table_name = 'TASK'
and a.owner = b.owner
and a.index_name = b.segment_name;

OWNER INDEX_NAME LEAF_BLOCKS NUM_ROWS BLOCKS
ORADBA TASK_APPT_FK 65 23771 175
ORADBA TASK_CENTER_FK 99 45975 145
ORADBA TASK_CLIENT_FK 101 45975 205
ORADBA TASK_ENC_FK 117 45975 215
ORADBA TASK_TYPE_FK 74 45975 195
ORADBA TASK_DT_REQ 84 45975 86

我们会发现 task_appt_fk 索引需要 66个块,但是现在却占有了175个块。因此,如果我们按照正确的大小重新建立索引,我们可以释放109个块。而所有的这些则是建立于先前运行的没有丢失任何用户的统计的基础之上的。我可以选择一天中的任何时间。

重新指定大小和碎片整理的逻辑步骤:

那么,我们使用这些新的发现以做得更好?这里有一个最好的案例:我们制作一个完整的表空间时,可以使重新设定大小和整理表空间碎片同时进行。具体步骤如下:

• 根据dba_indexes索引的大小,在不同得表空间中重新创建索引。这会去掉索引中任何被删除的空间。
• Coalesce表空间
• 通过分析这些索引得到基于分钟的统计
• 把索引重新创建回index tablespace
出自:http://www.itpub.net/thread-760206-1-1.html
请使用浏览器的分享功能分享到微信等