索引的特点
- 对于具有只读特性或较少插入、更新或删除操作的大表通常可以提高查询速度
- 可以对表的一列或多列建立索引
- 建立索引的数量没有限制
- 索引需要磁盘存储,需要Oracle自动维护
- 索引对用户透明,是否使用索引是Oracle决定的
全表扫描
Oracle读取表中所有行,此时通过多块都操作可以减少IO的次数,利用多块读可以提高全表扫描的速度,只有在全表扫描的情况下才能使用多块读,在较大的表上不建议使用全表扫描,如果读取表的数据总量超过5%-10%,那么通过全表扫描,并行查询可能会使得我们的路径采用全表扫描。
通过ID(RowID)
ROWID是数据行所存储的数据块地址,ROWID指出了数据文件、块号、行号,是最快查询数据的方式,这种方式不会使用多块读,而是采用单块读的方式。
使用索引
在索引中除了存储索引值,还存储了对应的ROWID。索引扫描的步骤:
- 扫描索引得到相应的ROWID
- 通过ROWID从表中读取相应的数据
数据的选择性越高(身份证),表数据在数据块中的分布越集中,索引的性能越好。
索引扫描类型
- 索引唯一扫描:主键、唯一键,Oracle通常返回一个数据行
- 索引范围扫描:在唯一键上使用染个操作符(>,<,<>,>=,<=,between);在组合索引,只使用部分列进行查询,导致查询多行;对非唯一索引列上进行查询
- 索引全表扫描:查询出的数据必须全部从索引中得到
- 索引快速扫描:扫描索引块中的所有数据,这点与索引全表扫描相似,但是索引快速扫描不进行数据的排序,在这种方式下可以使用多块读功能,也可以使用并行读功能,最大化数据的吞吐量。
限制索引的使用情况
- 使用不等于运算符(<>,!=):
- 使用 IS NULL或 IS NOT NULL:如果被索引的列在某些行中存在NULL值,在索引列中就不会有相应的条目,位图索引对于NULL列会进行记录,因此位图索引对于NULL搜索通常较快。
- 使用函数
- 比较不匹配的数据类型:
集群因子
简单的说,Index Clustering Factor是通过一个索引扫描一张表,需要访问的表的数据块的数量,即对I/O的影响,也代表索引键存储位置是否有序。
- 如果越有序,即相邻的键值存储在相同的block,那么这时候Clustering Factor的值就越低;
- 如果不是很有序,即键值是随机的存储在block上,这样在读取键值时,可能就需要一次又一次的去访问相同的block,从而增加了I/O。
Clustering Factor的计算方式如下:
(1)、扫描一个索引(large index range scan);
(2)、比较某行的rowid和前一行的rowid,如果这两个rowid不属于同一个数据块,那么cluster factor增加1;
(3)、整个索引扫描完毕后,就得到了该索引的clustering factor。
如果clustering factor接近于表存储的块数,说明这张表是按照索引字段的顺序存储的。如果clustering factor接近于行的数量,那说明这张表不是按索引字段顺序存储的。在计算索引访问成本的时候,这个值十分有用。Clustering Factor乘以选择性参数(selectivity)就是访问索引的开销。
如果这个统计数据不能真实反映出索引的真实情况,那么可能会造成优化器错误的选择执行计划。另外如果某张表上的大多数访问是按照某个索引做索引扫描,那么将该表的数据按照索引字段的顺序重新组织,可以提高该表的访问性能。
二元高度
二元高度就是Oracle数据库中帮助数据库管理员来做好这个工作的工具。索引二元高高度对把Rowid返回给用户进程时所要求的I/0数量起到非常关键的作用。数据库管理员只要了解这个即可,而不需要花费很长的时间去搞明白什么叫做二元高度。在Oracle数据库中,系统视图sys.dba_indexes就保存在索引的二元高度信息。如下图所示的SQL语句,可以查询处索引的二元高度的值。 字段Blevel表示二元高度;Index_name则表示索引的名称。一般来说,二元高度越低越好(最低为0)。作为数据库管理员,就是需要相方设法让这个二元高度的值变为0。虽然这个目标看起来比较简单,但是实现起来却有相当大的困难。
二元高度主要随着表中索引的非NULL值以及索引列中值的宽度而变化。如果索引列上大量的行被删除,那么他的二元高度不会降低,重建索引会降低,如果一个索引中被删除的行接近20%-25%,重建索引会降低二元高度。
直方图
在分析表和索引时,直方图用于记录数据的分布,通过获取该信息,基于成本的优化器就可以决定使用返回少量行的索引,而避免使用基于限制条件返回许多行的索引。直方图的使用不受索引的限制,我们可以在表的任何列上构建直方图(一般是在索引上构建)
构建直方图的最主要的原因就是:帮助优化器在表中数据严重倾斜时做出更好的选择。如果一个表中的列上(通常使用索引)数据发生严重的倾斜。那么在这个列上建立直方图将非常有意义,这样优化器就知道什么时候该使用索引,什么时候不需要使用索引。
建立索引
CREATE [UNIQUE|BITMAP] INDEX[schema.] index_name
---UNIQUE:说明该索引是唯一索引 BITMAP:创建位图索引
ON [schema.] table_name
(column_name[DESC|ASC][, column_name[DESC|ASC]]...) --- DESC|ASC:说明创建的索引
为降序或者升序排列
[REVERSE] --REVERSE:说明创建反向键索引
[TABLESPACE tablespace_name] ---- TABLESPACE:说明要创建
的索引所存储的表空间
[PCTFREE n] ----PCTFREE:索引块中预留的空间比例
[INITRANS n] ---INITRANS:每一个索引块中分配的事务数
[MAXTRANS n] ---MAXTRANS:每一个索引块中分配的最多事务数
[instorage state] --instorage state:说明索引中区段extent如何分配
[LOGGING|NOLOGGING] ----LOGGING|NOLOGGING:说明要记录(不记录)索引
相关的操作,并保存在联机重做日志中
[NOSORT] ---NOSORT:不需要在创建索引时按键值进行排序
查看索引
USER_IND_COLUMNS:查询索引列相关的信息
USER_INDEXES:查询索引信息,DROPPED表示该对象是否被删除
B树索引
索引的顶层为根,它包括指向索引中下一层次的条目。下一层次为分支块,它又指向位于索引中下一层索引中下一层次的块,最底层的是叶节点,它包含指向表行的索引条目。叶块是双向关联的,这边与按键值升序或降序扫描索引;
一个索引条目包含以下组件:
- 条目头:存储列数和锁定信息
- 键列长度/值对:用于定义键中的列大小,后面跟随列值(此类长度/值对的数目就是索引中的最大列数)。
在非分区表的B 树索引中:
- 当多个行具有相同的键值时,如果不压缩索引,键值会出现重复
- 当某行包含的所有键列为NULL 时,该行没有对应的索引条目。因此,当WHERE 子句指定了NULL 时,将始终执行全表扫描
对表执行DML 操作时,Oracle 服务器会维护所有索引。下面说明对索引执行DML 命令产生的效果:
- 执行插入操作导致在相应块中插入索引条目。
- 删除一行只导致对索引条目进行逻辑删除。已删除行所占用的空间不可供后面新的叶条目使用。
-
更新键列导致对索引进行逻辑删除和插入。PCTFREE 设置对索引没有影响,但创建时除外。即使索引块的空间少于PCTFREE 指定的空间,也可以向索引块添加新条目。
位图索引
位图索引使用位图标识被索引的列值,它适用于没有大量更新任务的数据仓库,因为使用位图索引时,每个位图索引项与表中大量的行有关联,当表中有大量更新、删除、插入时,位图索引相应地需要做大量的更改,而且索引所占用的磁盘空间也会明显增加,并且索引在更新时受影响的索引需要锁定,所以位图索引不适合大量更新操作的OLTP系统。
位图发挥最大威力的场合是:当一个表中包含了多个位图索引,Oracle就可以合并从每个位图索引得到的结果集,快速删除不必要数据,对于较低基数的位图索引来说,位图索引的尺寸远远小于B树索引,因此可以大大减少IO的数量。
对于位图索引的列,列值的数量要求较少或者中等(索引列基数较小)。如列的基数是4,Oracle为每个唯一键创建一个位图,然后把与键值相关联的ROWID保存为位图,最多可以包括30列。
对于非常大的表来说,在多个低基数列上建立位图索引是一个很好的选择。对于位图索引来说,即使从表中读取很多行,也会使用位图索引。例如在一个sex列上建立索引,每次从表中读取半数的数据行,但是还是会使用位图索引。
位图索引插入问题
- 位图索引在批处理(单用户)操作中加载表(插入操作)方面通常比B树做的好
- 当有多个会话同时向表中插入数据行时不应该使用位图索引
- 当每条记录都增加一个新值时,B-树索引要比位图索引快3倍
在B树索引中,可以实现行级锁,但是在位图索引中,因为对ROWID进行压缩存放(一个ROWID范围+位图),因此每次锁定的都是整个的ROWID范围,因此对表中的位图索引列进行更新的时候,并发性很差,容易导致死锁。select不会受到影响。
位图索引有很多限制: - 基于规则的优化器不会考虑位图索引
- 当执行alter table 语句并修改包括位图索引列时,会使得位图索引失效
- 位图索引不包含任何列数据,不能用于任何类型的完整性检查
- 位图索引不能被声明为唯一索引
- 位图索引最大的长度为30
Hash索引
使用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索引在有限制条件(需要指定一个确定的值而不是一个值范围)的情况下非常有用。
反向键索引
反向键索引是指在创建索引过程中对索引列创建的索引值的字节反向,使用反向键索引的好处是将值连续插入到索引中的反向键能避免争用。
情景:
1,insert繁忙,主键是用序列号(每次加1),主键是有索引的,用序列号生成,因此相邻的索引记录就可能存在于同一个数据块中,引起数据块竞争,导致性能下降。
2,随着时间增长,久值被删除,新值被插入,逐渐的序列号很大,索引树是往序列号大的一面偏,树的深度加深,所以索引效率低下,造成严重的性能问题。
反向键索引并不是一种被广泛使用的索引方式,主要适用于使用等值运算符“=”进行的查询。它的索引键值的分布是分散式的,并不是按照索引列的有序方式存储,因而无法进行范围化的查询。反向键索引的优势:一方面,它可以平衡I/O,有效地减少应用并发时的争用,另一方面,多数情况下数据在堆表中是按照插入的顺序而存储在邻近的位置上,所以数据块在内存中被再次使用的可能性是比较大的。总的来说,大多数情况下反向键索引带来的性能收益往往小于其所花费的代价,应用范围偏窄,在特殊情况下可以灵活使用。
基于函数索引
注意事项
- 基于函数的索引只能针对一种函数,其他函数不起作用
- 控制索引的数量,因为对DML有影响
监控索引的使用
- 启动监控:alter index *** monitoring usage
- 一定周期后关闭监控:alter index *** nomonitoring usage
- 查看监控情况:查询试图v$object_usage,也可以使用explan的输出和使用SQL trace等工具
重建索引
索引需要维护,不然如果建立了索引的表中有大量的删除和插入操作,会使得索引很大,因为在删除操作后,删除值所占用的索引空间不能被索引自动重新使用,而插入操作会不断使得索引变大,对于大表和DML操作很频繁的表,索引的维护是很重要的。Oracle提供了一个Rebuild命令来重建索引,使得索引空间可以重用删除值所占用的空间,使得索引更加紧凑。
使用索引重建不会影响索引的使用,但是有些限制条件如果不能使用DML和DDL操作,如果使用联机重建索引的方式,可以执行DML操作,但是不能执行DDL操作。
alter index idx_a rebuild;
alter index idx_a rebuild tablespace idx_tb;
alter index idx_a rebuild pctfree 30 storage (next 100k);
alter index idx_a rebuild online;
维护索引:修改索引的各种参数
增加索引磁盘空间:增加后可以通过查询user_segments查看
alter index idx_a allocate extend;
合并索引碎片:可以释放磁盘空间
alter index idx_a coalease;
删除索引
如果经过索引监控发现索引无效,或者处理效率考虑暂时删除该索引,则使用drop index即可。