oracle学习之索引的介绍


在关系型数据库中,用户查找数据与行的物理位置无关紧要。为了能够找到数据,表中的每一行均用一个rowid来标识,rowid能够标识数据库中某一行的具体位置。当Oracle数据库中存储海量的记录时,就意味着大量的rowid标识,这样Oracle如何能够快速找到指定的rowid呢?这时就需要使用索引对象,它可以提供服务器在表中快速查找记录的功能。   1、索引概述 如果一个数据表中存有海量的数据行,当对表执行指定条件的查询时,常规的查询方法会将所有的记录都读取出来,然后再把读取的每一条记录与查询条件进行比对,最后返回满足条件的记录。这样进行操作的时间开销和IO开销都很大。对于这种情况,就可以考虑通过建立索引来减少系统开销。 如果要在表中查询指定的记录,在没有索引的情况下,必须遍历整个表,而有了索引之后,只需要在索引中找到负荷查询条件的索引字段值,就可以通过保存在索引中的rowid快速找到表中对应的记录。例如,如果将表看做一本书,索引的作用则类似与书中的目录。在没有目录的情况下,要在书中查找指定的内容必须翻阅全书,而有了目录之后,只需要通过目录就可以快速找到包含所需要内容的页码(相当于rowid)。   Oracle对所以与表的管理有很多相同的地方,不仅需要在数据字典里保存所以的定义,还需要在表空间中为它分配实际的存储空间。创建索引时,Oracle会自动在用户的默认表空间或指定的表空间创建一个索引段,为索引数据提供空间。   用户可以在Oracle中创建多种类型的索引,以适应各种表的特点。按照索引数据的存储方式可以将索引分为B树索引、位图索引、反向键索引和基于函数的索引。按照索引列的唯一性可以分为唯一性索引和非唯一性索引;按照索引列的个数可以分为单列索引和复合索引。   建立和规划索引时,必须选择合适的表和列,如果选择的表和列不合适,不仅无法提高查询速度,反而会极大降低DML操作的速度,所以建立索引必须注意一下几点: a)索引应该建立在where子句频繁引用表列上,如果在达标上频繁使用某列或某几列作为条件执行索引操作,并且检索行数低于总行数15%,那么应该考虑在这些列上建立索引。 b)如果经常需要基于某列或某几个列执行排序操作,那么这些列上建立索引可以加快数据排序速度。 c)限制表的索引个数。索引主要用于加快查询速度,但会降低DML操作的速度。索引越多,DML操作速度越慢,尤其会极大地影响INSERT和DELETE操作的速度。因此,规划索引时,必须仔细权衡查询和DML的需求。 d)指定索引块空间的使用参数。基于表建立索引时,Oracle会将相应表列数据添加到索引块。为索引块添加数据时,Oracle会按照pctfree参数在索引块上预留部分空间,该预留部分空间是为将来的insert操作准备的。如果将来在表上执行大量Insert操作,那么应该在建立索引时设置较大的pctfree。 e)将表和索引部署到相同的表空间,可以简化表空间管理;将表和索引部署不同的表空间,可以提高访问性能。 f)当大表上建立索引时,,使用nologging选项可以最小化重做记录。使用nologging选项可以节省重做日志空间、降低索引建立时间、提高索引并行建立的性能。 g)不要在小表上建立索引 h)为了提高多表连接的性能,应该在连接列上建立索引。 index entry的组成部分 indexentry entry header    存放一些控制信息 key column length             某一个key的长度 key column value               某一个key的值 rowid                                  指针,具体指向某一个数据   2、创建索引 在创建索引时,Oracle首先对将要建立索引的字段进行排序,然后将排序后的字段值和对应的记录的rowid存储在索引段中。建立索引可以使用create index语句,通常由表的所有者来建立索引。如果要以其他用户身份建立索引,则要求用户必须具有create any index系统权限或者相应表的index对象权限。 2.1、建立B树索引 B树索引时Oracle数据库最常用的索引类型(也是默认的),它是以B树结构组织并存放索引数据的,默认情况下,B树索引中的数据是以升序方式排列的。如果表包含的数据非常多,并且经常在where子句中引用某列或某几个列,则应该基于该列或这个几个列建立B树索引。B树索引由根块、分支块和叶块组成,其中主要数据都集中在叶子节点。 根块:索引顶级块,它包含指向下一级节点的信息。   分支块:它包含指向下一级节点(分支块或叶块)的信息。 叶块:通常也称为叶子块,它包含索引入口数据,索引入口包含索引列的值和记录行对应的物理地址rowid。 在B树索引中无论用户要搜索哪个分支的叶块,都可以保证 所经过的索引层次是相同的。Oracle采用这种方式的索引,可以确保无论索引条目位于何处,都只需要话费相同的IO即可获取它,这就是为什么被称为B树索引(B是英文BALANCED的缩写)。 例如,使用这个B树索引搜索编号为A004节点时,首先要访问根节点,从根节点中可以发现下一步应该搜索左边的分支,由于值A004小于B001,因此不需要第二次读取数据,而直接读取左边的分之节点。从左边的分之节点,可以判断出,要搜索的索引条目位于右侧的第一个叶子节点中。在那里可以很快找到要查询的索引条目,并根据索引条目中的rowid进而找到所有要查询的记录。 例子: create index emp_deptno_index on (deptno) pctfree 25 tablespace test; PCTFREE子句指定为将来槽所预留的空闲空间,子句tablespace用于指定索引段的表空间。假设表已经包含大量数据,那么在建索引时应该仔细规划pctfree的值,以便为以后的Insert操作预留空间。   2、建立位图索引 索引的作用简单来说就是能够通过给定的索引列值,快速地找到对应的记录。在B树索引中,通过在索引中保存的索引列的值以及记录的物理地址rowid来实现快速查找。但是对于一些特殊的表,B树索引的效率可能会很低。例如,在某个具有性别列的表,该列的所有取值只能是男或者女,如果在性别列上创建B树索引,那么创建的B数只有两个分支,,那么使用该索引对该表进行检索时,将返回一半的记录,这样就失去了所以的基本作用。 当列的基数很低时,为其建立B树索引显然不合适。“基数低”表示在索引列中,所有取值的数据量比表中行的数量少。如性别列只有两个取值;再比如某个用于10000行的表,它的一个列包含100个不同的取值,则该列还满足低基数的要求,因为该列与行数的比例为1%,Oracle推荐当一个列的基数小于1%时,这些列不再合适建立B树索引,而适用于位图索引。但在表中低基数的列上建立位图索引时,系统将对表进行一次全表扫描,为遇到的个取值构建图标。   2.1位图索引的原理 如果在性别列上建立了位图索引,对于性别这个列,针对每行的rowid(rowid可理解为每行的物理位置),位图索引形成两个向量,男向量位10100***,向量的每一位表示改行是否是男,如果是则位1,否为0,同理,女向量位01011***,(可理解为给每行数据的性别列中位产生两个向量,分别为男向量和女向量:男向量中值为男用1表示,值不是男用0表示;同理女向量中值为女用1表示,值不是女的用0表示) rowid    1      2     3     4     5    ***** 男          1      0     1     0     0 女          0      1     0     1     1   2.2、位图索引检索数据的过程 当我们使用查询语句select * from table where a ='男' and b='未婚';的时候,首先去除男向量,然后取出未婚的向量,将两个向量做and操作,这时生成新向量00100,为1时则表示满足查询要求的数据。   2.3、位图索引的限制或者弊端 位图索引在读密集的环境中能很好地工作,但是对于写密集的环境则极不合适,原因在于,一个位图索引键值指向多行。如果一个会话修改了有索引的列的数据,那么大多数情况下,这个索引键值(索引条目)指向的所有行都会被锁定。Oracle无法锁定一个位图索引键值的单独一位,而会锁定整个位图索引条目,倘若其他会话修改也需要更新同意的这个位图索引条目,就会被卡主,这样就大大影响了并发性。 位图索引时为数据仓库(也就是查询环境设计的),位图索引特别不适合OLTP系统,位图索引不适合与DML频繁的环境,适用于DSS系统,不适合频繁修改的系统,弊端是严重影响并发性。因为update索引列值的时候,会锁定新值和旧值指向的所有数据行,所以使用位图索引要慎重。   2.4、创建位图索引 create bitmap index emp_saary_bmp on employees(salary) tablespace test; 初始化参数create_bitmap_area_size用于指定建立位图索引时分配的位图区大小,默认值为8M,该数值越大建立位图索引的速度越快。为了加快创建位图索引,应将该参数设置更大些。因为该参数是静态参数,所以修改后必须重启数据块才能生效。   3、反向键索引 Oracle中系统会自动为表的主键建立索引,这个默认是B树索引,通常用户希望表的主键是一个自动增长的序列编号,这样的列就是所谓的单挑递增序列编号列。当在这种顺序递增的列上建立普通的B数索引时,如果表的数据量非常大,将导致索引数据分布不均。随着数据行的插入,索引数的层级增长很快,搜索索引发生的IO读写次数和索引树的层级数成正比,也就是说,一棵具有5个层级的B树索引,在最终读取到索引数据时最多可能发生多大5次IO操作,因而减少索引的层级树是索引性能调整的一个重要方法。如果索引列的数据以严格的有序的方式插入,那么B树索引树将变成一棵不对称的歪树。 反向键索引时一种特殊类型的B树索引,在顺序递增列上建立索引时非常有用。反向键索引的原理非常简单,在存储结上与常规的B树索引相同,然而,如果用户使用序列在表中输入记录,则反向键索引首先指向每个列键值的字节,然后在反向后的新数据上进行索引。例如,索引列键值为2011则反向转换为1102,9527反向为7259,显然经过反向处理的有序数据变得比较随机了,这样得到的索引树就比较对称,从而提高表的查询性能。 但是方向键索引也有局限性:如果在查询条件中有between、<、>等,其方向键索引无法使用,将执行全表扫描;只有对反向键索引列进行=的比较操作时,其反向键才会得到使用。 创建示例: create index emp_job_reverse on emp(job) reverse tablespace test; 原索引为B树索引,修改为反向键索引: alter  index emp_deptno_index rebuild reverse;   4、基于函数的索引 用户在使用Oracle数据库时,最常遇到的问题之一就是他对字符大小写敏感。可以通过使用Oracle字符串函数对其进行转换,然后再进行检查。但是,使用这样的查询时,用户不是基于表中存储的记录进行搜索的,即如果搜索的值不存在表中,那么它就一定也不会在索引中,所以即使在列上建立索引,Oracle也会被迫执行全表扫描,并为所遇到的个行进行函数运算。为了解决这个问题,Oracle提供了一种新的索引类型--基于函数的索引,它也是常规B树索引,但它存放的数据是由表中的数据应用函数后所得到的,而不是直接存放表中的数据本身。 示例: create  index emo_job_fun on enp(lower(job));   5、修改索引 修改索引一般是通过alter index语句来完成。在表建立索引后,随着对表不断进行更新、插入和删除操作,索引中会产生越来越多的存储碎片,这将对索引的工作效率产生负面影响,这时可以采取两种方式来清楚碎片,重建索引或合并索引。 alter index emp_deptno_index coalesce deallocate unused;        ------------索引碎片整理(coalesce)收回未用到的空间(deallocate unused); 假设在操作前,B树索引的前两个叶子块分别有70%和30%的空闲空间,合并索引后,可以将他们的数据合并到一个索引叶子块中。   消除索引碎片的另一个方式是重建索引,重建索引可以使用ALTER INDEX  INDEX_NAME REBUILD,重建操作不仅可以消除存储碎片,还可以改变索引的存储表空间,然后再删除原理的索引。 示例: alter index emp_deptno_index rebuild tablespace test ;------重建索引并指定表空间   6、删除索引 删除索引使用DROP INDEX INDEX_NAME,需要删除某个索引的情况主要有以下几种: a)如果移动了表中的数据,导致索引中包含过多的存储碎片,此时需要删除并重建索引。 b)通过一段时间的监视,发现很少有查询会使用到该索引。 c)该索引不再需要时应该删除索引,以释放其所占用的空间。 索引被删除后,它所占用的空间都将返回给包含它的表空间,并可以被表空间中的其他对象使用。如果索引时定义约束时由Oracle系统自动建立的,则必须禁用或者删除约束本身,另外在删除一个表时,Oracle也会删除所有与表有关的索引。 关于索引最后需要注意一点,虽然一个表可以用于很多索引,但是表中的索引数据越多,维护索引的成本就越大,每当向表中插入删除更新一条记录时,Oracle都必须对该表的所有索引进行更新。因此,用户还需要在表的查询速度和更新速度之间找到一个合适的平衡点,也就是说根据表的实际情况限制在表中创建索引的数量。   7、显示索引的信息 select index_name,index_type from dba_indexes where owner='SCOTT';  ---查看用户下索引的信息 select column_name,column_length from user_ind_columns where index_name='EMP_DEPTNO_INDEX';---查看索引的表的列信息 select tablespace_name,segment_type,bytes from user_segments where segment_name='EMP_DEPTNO_INDEX';  ----查看索引段的信息 select column_expression from user_ind_expressions where index_name='EMP_JOB_FUN'; ----查看基于函数的索引信息