oracle 索引

索引是建立在表的一列或多个列上的辅助对象,目的是加快访问表中的数据。Oracle存储索引的数据结构是B树,位图索引也是如此,只不过是叶子节点不同。B数索引由根节点、分支节点和叶子节点组成,上级索引块包含下级索引块的索引数据,叶节点包含索引数据和确定行实际位置的rowid。
当查询返回的记录数排序表<40%非排序表 <7%且表的碎片较多(频繁增加、删除)时可以加快查询速度减少I/O操作消除磁盘排序
索引逻辑上分为:
单行索引/多行索引和唯一索引/非唯一索引。
索引物理上分为:B树索引、位图索引、反向索引、HASH索引、于函数的索引
分区索引/非分区索引和域索引。
3.4.1B树索引
Oracle数据库中最常见的索引类型是B-TREE索引,也就是B-树索引,以其同名的计算科学结构命名。使用CREATE INDEX语句时,默认就是在创建B-TREE索引。没有特别规定可用于任何情况。
1.特点:
适合大量的增、删、改(OLTP)操作。
(1)不能用包含OR操作符的查询。
(2)适合高基数的列(唯一值多)。
(3)典型的树状结构。
(4)每个结点都是数据块。
(5)大多都是物理上一层、两层或三层不定,逻辑上三层。
(6)叶子块数据是排序的,从左向右递增。
(7)在分支块和根块中放的是索引的范围。
2.技巧
索引列的值都存储在索引中。因此,可以建立一个组合(复合)索引,这些索引可以直接满足查询,而不用访问表。不用从表中检索数据,从而减少了I/O量。
3.4.2位图索引
位图索引非常适合于决策支持系统(Decision Support System,DSS)和数据仓库,它们不应该用于通过事务处理应用程序访问的表。可以使用较少到中等基数(不同值的数量)的列访问非常大的表。尽管位图索引最多可达30个列,但通常它们都只用于少量的列。
例如,表包含一个称为性别的列,它有两个可能值:男和女。这个基数只为2,如果用户频繁地根据性别列的值查询该表,就把改例列设置为位图索引的基列。如果有多个可用的位图索引,Oracle就可以合并从每个位图索引得到的结果集,快速删除不必要的数据。
(1)特点:
适合决策支持系统。
做UPDATE代价非常高。
非常适合OR操作符的查询。
基数比较少的时候才能建位图索引。
(2)技巧:
对于有较低基数的列需要使用位图索引。位图对于低基数(少量的不同值)列来说非常快,这是因为索引的尺寸相对于B树索引来说小了很多。因为这些索引是低基数的B树索引,所以非常小,可以经常检索表中超过半数的行,并且仍使用位图索引。
当大多数条目不会向位图添加新的值时,位图索引在批处理(单用户)操作中加载表(插入操作)方面通常要比B树做得好。当多个会话同时向表中插入行时,不应该使用位图索引,在大多数事务处理应用程序中都会发生这种情况。
在一个查询中合并多个位图索引后,可以使性能显著提高。位图索引使用固定长度的数据类型要比可变长度的数据类型好。较大尺寸的块也会提高对位图索引的存储和读取性能。
3.4.3反向索引
  这个索引不常见,但是特定情况特别有效,比如一个5位字符字段员工编号值(10001,10002,10033,10005,10016…)这种情况默认索引分布过于密集,不能利用好服务器的并行,但是反向之后变成10001,20001,33001,50001,61001就有了一个很好的分布,能高效的利用好并行运算。
特点:不可以将反转键索引与位图索引或索引组织表结合使用。因为不能对位图索引和索引组织表进行反转键处理。
技巧:如果磁盘容量有限,同时还要执行大量的有序载入,就可以使用反转键索引。
3.4.4哈希索引
 使用HASH索引必须要使用HASH集群。建立一个集群或HASH集群的同时,也就定义了一个集群键。这个键告诉Oracle如何在集群上存储表。在存储数据时,所有与这个集群键相关的行都被存储在一个数据库块上。如果数据都存储在同一个数据库块上,并且将HASH索引作为WHERE子句中的确切匹配,Oracle就可以通过执行一个HASH函数和I/O来访问数据,而通过使用一个二元高度为4的B树索引来访问数据,则需要在检索数据时使用4个I/O。其中的查询是一个等价查询,用于匹配HASH列和确切的值。Oracle可以快速使用该值,基于HASH函数确定行的物理存储位置。
HASH索引可能是访问数据库中数据的最快方法,但它也有自身的缺点。集群键上不同值的数目必须在创建HASH集群之前就要知道,需要在创建HASH集群的时候指定这个值。低估了集群键的不同值的数字可能会造成集群的冲突(两个集群的键值拥有相同的HASH值)。这种冲突是非常消耗资源的。冲突会造成用来存储额外行的缓冲溢出,然后造成额外的I/O。如果不同HASH值的数目已经被低估,就必须在重建这个集群之后改变这个值。
ALTER CLUSTER命令不能改变HASH键的数目。HASH集群还可能浪费空间。如果无法确定需要多少空间来维护某个集群键上的所有行,就可能造成空间的浪费。如果不能为集群的未来增长分配好附加的空间,HASH集群可能就不是最好的选择。如果应用程序经常在集群表上进行全表扫描,HASH集群可能也不是最好的选择。由于需要为未来的增长分配好集群的剩余空间量,全表扫描可能非常消耗资源。
在实现HASH集群之前一定要小心。需要全面地观察应用程序,保证在实现这个选项之前已经了解关于表和数据的大量信息。通常HASH对于一些包含有序值的静态数据非常有效。
技巧:HASH索引在有限制条件(需要指定一个确定的值而不是一个值范围)的情况下非常有用。
3.4.5函数索引
可以在表中创建基于函数的索引。如果没有基于函数的索引,任何在列上执行了函数的查询都不能使用这个列的索引。例如,下面的查询就不能使用JOB列上的索引,除非它是基于函数的索引,具体代码如下:
select * from emp where UPPER(job) = ‘MGR’;
下面的查询使用JOB列上的索引,但是它将不会返回JOB列具有Mgr或mgr值的行,具体代码如下:
select * from emp where job = ‘MGR’;
可以创建这样的索引,允许索引访问支持基于函数的列或数据。可以对列表达式UPPER(job)创建索引,而不是直接在JOB列上建立索引,如:
create index EMP$UPPER_JOB on emp(UPPER(job));

函数索引限制:
(1)必须使用一个确定的函数定义基于该函数的索引,也就是说函数仅返回一个值。
(2)必须使用返回可重复值的函数来定义基于该函数的索引,如sysdate就不行。
(3)可以对基于函数的索引进行分区,但是对基于函数的全局分区索引来说,分区键不能是索引所基于的函数。
(4)函数必须使用圆括号来定义,即使没有参数。
(5)索引基于的函数不能包含聚合函数 。
使用建议:
不要轻易在字段前面加函数;
尽量不要将字段嵌入表达式中;
小结:尽量减少使用函数索引,能不用就不用,因为函数索引的维护代价比普通索引高;函数索引计算值可能大于原字段值,将消耗更多的存储空间。

3.4.6分区索引和全局索引
  分区索引就是简单地把一个索引分成多个片断。通过把一个索引分成多个片断,可以访问更小的片断(也更快),并且可以把这些片断分别存放在不同的磁盘驱动器上(避免I/O问题)。B树和位图索引都可以被分区,而HASH索引不可以被分区。可以有好几种分区方法:表被分区而索引未被分区;表未被分区而索引被分区;表和索引都被分区。不管采用哪种方法,都必须使用基于成本的优化器。分区能够提供更多可以提高性能和可维护性的可能性
有两种类型的分区索引:本地分区索引和全局分区索引。每个类型都有两个子类型,有前缀索引和无前缀索引。表各列上的索引可以有各种类型索引的组合。如果使用了位图索引,就必须是本地索引。把索引分区最主要的原因是可以减少所需读取的索引的大小,另外把分区放在不同的表空间中可以提高分区的可用性和可靠性。
在使用分区后的表和索引时,Oracle还支持并行查询和并行DML。这样就可以同时执行多个进程,从而加快处理这条语句。
可以使用与表相同的分区键和范围界限来对本地索引分区。每个本地索引的分区只包含了它所关联的表分区的键和ROWID。本地索引可以是B树或位图索引。如果是B树索引,它可以是唯一或不唯一的索引。
这种类型的索引支持分区独立性,这就意味着对于单独的分区,可以进行增加、截取、删除、分割、脱机等处理,而不用同时删除或重建索引。Oracle自动维护这些本地索引。本地索引分区还可以被单独重建,而其他分区不会受到影响。
全局分区索引在一个索引分区中包含来自多个表分区的键。一个全局分区索引的分区键是分区表中不同的或指定一个范围的值。在创建全局分区索引时,必须定义分区键的范围和值。
全局索引只能是B树索引。Oracle在默认情况下不会维护全局分区索引。如果一个分区被截取、增加、分割、删除等,就必须重建全局分区索引,除非在修改表时指定 ALTER TABLE命令的UPDATE GLOBAL INDEXES子句。
没有什么因素能限制索引的对等分区,但Oracle在生成查询计划或执行分区维护操作时,并不会充分利用对等分区。如果索引被对等分区,就必须把它创建为一个本地索引,这样Oracle可以维护这个索引,并使用它来删除不必要的分区,如下图所示。在该图的 3个索引分区中,每个分区都包含指向多个表分区中行的索引条目。
如果一个全局索引将被对等分区,就必须把它创建为一个本地索引,这样 Oracle可以维护这个索引,并使用它来删除不必要的分区。

3.4.7创建索引
创建索引的格式如下:
CREATE UNIUQE | BITMAP INDEX .

  ON .
       ( |  ASC | DESC,
         |  ASC | DESC,...)
 TABLESPACE 
 STORAGE 
 LOGGING | NOLOGGING
COMPUTE STATISTICS
 NOCOMPRESS | COMPRESS
 NOSORT | REVERSE
 PARTITION | GLOBAL PARTITION

UNIQUE | BITMAP:指定UNIQUE为唯一值索引,BITMAP为位图索引,省略为B-TREE索引。
| ASC | DESC:可以对多列进行联合索引,当为expression时即基于函数的索引。
TABLESPACE:指定存放索引的表空间(索引和原表不在一个表空间时效率更高)。
STORAGE:可进一步设置表空间的存储参数。
LOGGING | NOLOGGING:是否对索引产生重做日志(对大表尽量使用NOLOGGING来减少占用空间并提高效率)。
COMPUTE STATISTICS:创建新索引时收集统计信息。
NOCOMPRESS | COMPRESS:是否使用键压缩(使用键压缩可以删除一个键列中出现的重复值)。
NOSORT | REVERSE:NOSORT表示与表中相同的顺序创建索引,REVERSE表示相反顺序存储索引值。
PARTITION | NOPARTITION:可以在分区表和未分区表上对创建的索引进行分区。
1.普通索引,创建格式如下:
CREATE INDEX 索引名 ON 表名(列名);
2.唯一索引,创建格式如下:
create unique index on ();
3.位图索引,创建格式如下:
create bitmap index on ()
4.组合索引,创建格式如下:
create index on ()
5.基于函数索引,创建格式如下:
create index on (column_name) reverse;create index on (upper(column_name))
6.反向键索引,创建格式如下:
create index on (column_name) reverse;
7.重置索引,创建格式如下:
alter index rebuild;
8.删除索引,创建格式如下:
drop index
3.4.8索引失效情况

1.查询条件中使用不等于操作符(<>, !=)
  下面查询语句,即使列dept_id有一个索引,查询语句仍然执行一次全表扫描,代码如下:
select * from dept where staff_num <> 1000;
  
 通过把用 or 语法替代不等号进行查询,就可以使用索引,以避免全表扫描。上面的语句改成下面这样的,就可以使用索引了,代码如下:
select * from dept shere staff_num < 1000 or dept_id > 1000;
2.使用 is null 或 is not null
使用 is null 或is nuo null也会限制索引的使用,因为数据库并没有定义null值。如果被索引的列中有很多null,就不会使用这个索引(除非索引是一个位图索引)。在sql语句中使用null会造成很多麻烦。
解决这个问题的办法就是建表时把需要索引的列定义为非空(not null)。
3.使用函数
 如果没有使用基于函数的索引,那么where子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。下面的查询就不会使用索引,代码如下:

select * from staff where trunc(birthdate) = ‘01-MAY-82’;
但是把函数应用在条件上,索引是可以生效的,把上面的语句改成下面的语句,就可以通过索引进行查找。
select * from staff where birthdate < (to_date(‘01-MAY-82’) + 0.9999);
4.比较不匹配的数据类型
  比较不匹配的数据类型也是难于发现的性能问题之一。下面的例子中,dept_id是一个varchar2型的字段,在这个字段上有索引,执行下面的语句会全表扫描,代码如下:
select * from dept where dept_id = 900198;
  这是因为Oracle会自动把where子句转换成to_number(dept_id)=900198,这样就限制了索引的使用。把SQL语句改为如下形式就可以使用索引
select * from dept where dept_id = ‘900198’;
5.使用like子句
 使用like子句查询时,数据库需要把所有的记录都遍历来进行判断,索引不能发挥作用,这种情况也要尽量避免。
Like 的字符串中第一个字符如果是‘%’则用不到索引
Column1 like ‘aaa%’ 是可以的
Column1 like ‘%aaa%’用不到
6.使用in
 尽管In写法要比exists简单一些,exists一般来说性能要比In要高的多。
用In还是用Exists的时机,当in的集合比较小的时候,或者用Exists无法用到选择性高的索引的时候,用In要好,否则就要用Exists,具体代码如下:

–使用in 查询数据
select count() from person_info where xb in (select xb_id from dic_sex);
–使用exists 查询数据
Select count(
) from n_acntbasic a where shbxdjm =:a and exists(select 1 from person_info where pid=a.pid and …);

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

你可能感兴趣的:(oracle,数据库,索引)