目录
一、索引的概述
二、限制索引使用的情况
1、使用不等于运算符(<>、!=)
2、使用 is null 或 is not null
3、使用函数
4、比较不匹配的数据类型
三、索引的类型
(一) B*Tree 索引 (是 oracle 默认的索引类型)
(二) 索引组织表
1、IOT 适用的场合有:
2、创建索引组织表
(三) 唯一索引
(四)反向键索引
(五) 降序索引
(六) 位图索引
(七) 基于函数的索引
(八) 快速重建索引
四、确定需要建立索引的列
创建索引的准则
五、监控索引的使用
六、删除索引
索引是一种可以选择创建的数据库对象,它主要用于提高查询性能。数据库索引把用户感兴趣的列值连同其行标识符 (ROWID) 存储 在一起。ROWID 包含了存储列值的表行在磁盘上的物理位置,索引中 记录了 rowid,因此 oracle 就可以根据索引中的 rowid 来判断记录
是否是在同一个 block 中,通过下面语句找到记录对应的block 号。
Select dbms_rowid rowid_block_number(rowid) from dual ;
Oracle 可以通过最少量的磁盘读取,有效地检索表中的数据。如 果没有可用的索引,Oracle 就必须读取表中的每一行,才能确定该行是否包含所需的信息。
索引是有代价的,索引会消耗磁盘空间和系统资源。在列值被修 改的同时也必须更新相应的索引。因此,索引使用了存储空间、I/O、 CPU 和内存资源。创建十分糟糕的索引,会浪费磁盘空间,并且会过 度消耗系统资源,也会导致数据库的性能下降。所以,不能为所有的 表和列组合创建索引。一般来说,增加索引会降低 INSERT 语句的性 能,因为需要同时对表和索引进行操作。索引列上的 UPDATE 操作将 会比没有加索引慢很多,因为数据库必须管理对表和索引的改动。此 外,大量行的 DELETE 操作将会由于表中存在索引而变慢。所以,需 要平衡索引带来的查询性能的提升和对数据修改性能的影响。
所以,必须了解索引,知道有什么类型的索引可用,应该选择哪 些表和列的组合来创建索引。
当访问表的数据时,Oracle 提供了两种方式:从表中读取所有行 (即全表扫描),或者通过 ROWID 一次读取一行。当访问大数据量表中 的少量行时,可以使用索引。如果只访问大数据量表中 5%的行,可 以使用索引标识需要读取的数据块,这样花费的 I/O 较少。如果没有 使用索引,就要读取表中所有的数据块。
通过 dba_indexes 视图查询表的所有索引,user_indexes 视图查 询当前方案(schema)的索引,all_indexes 视图查看能够访问的所有表的索引。
create index emp_id2 on emp (sal);
当执行这些命令时,数据库将在 emp 表上创建索引。索引将包含 emp 表中的特定值以及匹配这些值的行的 ROWID。如果需要查找 sal 值为 1000 的 emp 表中的记录,优化器就会使用 emp_id2 索引查找该
值,并找到相关的 ROWID,接着使用该 ROWID 在表中查找对应的行。
select table_name, index_name from user_indexes where table_name = 'EMP' ;
查询 user_ind_columns、dba_ind_columns 和 all_ind_columns 视图查询表中被索引的列的信息。
在很多情况下,即使创建了索引,也会导致索引不能使用,下面看一下限制使用索引的几种情况:
当在 WHERE 子句中使用不等于运算符 (<>、 !=) 时,其中被用到的列上的索引将无法使用。
当分析表的时候,oracle 收集表中数据分布的相关统计信息,基 于成本的优化器决定在 where 子句中对一些值使用索引,而对其它的 值不使用索引,因此,不是说在一个列上建立了索引就一直会使用索引,根据不同值,优化器会确定是否使用索引。
在 where 子句中使用 is null 或 is not null 同样会限制索引的使用,如果被索引的列在某些行中存在null值,在索引列中就不会有相应的条目。位图索引对于null 列会进行记录,因此位图索引对于 null搜索通常较为快速。
在创建表时对列指定 NOT NULL 或 DEFAULT 属性,可以帮助避免 可能出现的性能问题。
如果不使用基于函数的索引,那么在 SQL 语句的 where 子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。一些最常见 的函数,如 trunc、substr、to_date、to_char 和 instr 等都可以改变列的值,因此,被引用的列上的索引将无法使用。
比较不匹配的数据类型,oracle 不会对那些不兼容的数据类型报错 ,oracle 会做隐式数据转换 。例如 ,oracle 可以隐式地转换varchar2 类型的列中的数据去匹配数值类型数据。
Oracle 提供了丰富的索引类型,正确地使用索引可以产生良好的性能。Oracle 索引的类型如下:
不指定任何选项的情况下,CREATE INDEX 语句创建 B 树索引,需 要提供索引的名称、表名和列名。对于大多数应用程序,B 树索引都 是非常有效的。B 树索引有多个子类型:索引组织表、唯一索引、反 向键索引、键压缩索引、降序索引。
树索引结构有三个基本组成部分,根节点、分支节点、叶子节点,其 中根节点位于索引结构的最顶端,而叶子节点位于索引结构的最底 端,中间为分支节点。
在叶子节点中存储了实际的索引列的值和该列所对应的记录的行 ID ,即 ROWID ,ROWID 是唯一的 oracle 指针,指向该行的物理位置, 使用 ROWID 是 oracle 数据库中最快访问行的方法。叶子节点其实是 一个双向链表,每个叶子节点包含一个和上一个叶子点的指针,这样
在一定范围内遍历索引以搜索需要的记录。
图中,上面虚线筐内的数据块叫 Branch Block (分支块) ,它提供 到具体的 Leaf Block (页块或者叶子节点,即下面虚线筐内的数据块) 数据块的‘导航’,真正的索引数据是存储在 Leaf Block 上的。以查 找名为 Karthy 数据行为例:从根结点开始 (第一个 I/O) 找到第二层 中从左侧第二个分支 (指针) ,从该分支开始 (第二个 I/O) 找到第 三层页块中的最右侧分支 (指针) ,然后再进行第三个 I/O 找到该页 块,从中找到名为 Karthy 的索引条目,根据里面存储的 ROWID 即可 找到名为 Karthy 表记录(这需要另外一个 I/O) 。需要特别指出的是, 索引的叶子节点实际上是构成一个双向链表 (如上图所示) ,所以一 旦导航到叶子节点之后 (即发现第一个索引键值) ,执行索引键值的 有序扫描就变得很容易,‘索引键值的有序扫描’就是我们经常在执 行计划中看到 Index Range Scan (索引范围扫描) ,执行 Index Range Scan 时,ORACLE 不用反复在索引结构中做从根节点到叶子节点的导 航,而只是在第一次到达页块后,根据需要在叶子节点中沿双向链表 向前或者向后扫描就可以了。所以,如果使用了 B*树索引,ORACLE 要 满足诸如以下的谓词条件就相当简单:
where xxx between 1000 and 2000
ORACLE 发现第一个最小键值大于或等于 1000 的页子节点后,就 会水平的通过双向链表遍历其它页子节点,直到最后发现一个大于 2000 的值为止。整个过程只需从根节点到叶子节点‘导航’一次。 B*树索引结构中,所有的叶子都在同一层上,整个索引结构数据块的
层数称之为索引的高度 (Height) ,在图 1 中我们可以看到这个索引 的高度是 3 。由图 1 可知,无论根据索引查找表中的哪一条记录,所
执行的 I/O 此次数都是相同的。即对于诸如如下形式的查询:
select btree indexed col from t where btree indexed col=:x
无论:X 的值是多少,ORACLE 都将会执行同样的 I/O 次数来找到 该值。这就是 B*Tree 索引中的‘B’的含义,即 Balanced (平衡) 之 意。换句行话说,B*Tree 索引是高度平衡的 (HeightBalanced) 。 一个表哪怕有 1000 万条记录,只要其索引高度是 3 ,那么找到其中 一条记录所执行的 I/O 次数,与从具有同样索引高度的 1000 条记录 的表中找到一条记录所执行的 I/O 次数是一样的。这就是 B*Tree 索 引的魅力所在,也就是因为这一点使它成为一种非常通用的索引,目 前几乎所有关系数据库都支持它。
另外,ORACLE 在表示从索引根结点到叶子节点块遍历所涉及的 数据块数时使用了两个稍微不同的技术术语。一个就是前面提到的高 度 (Height) ,这是从根节点到叶子结点所需遍历的块数。另一个术 语是分支层数 (Blevel) ,它等与 Height-1 ,即它不把叶子节点层计
算在内。下面通过一个例子来加深对这两个术语的理解。
SQL> drop table t purge;
SQL> create table t(id number,name varchar(30));
declare i NUMBER:=0;
begin
loop
i:=i+ 1;
insert into t values (i,'NAME');
exit when i > 149999;
end LOOP;
END;
/
SQL> commit;
SQL> create unique index idx_t on t(id) pctfree 90;
SQL> select index_name,blevel,num_rows from user_indexes where table_name='T';
INDEX NAME BLEVEL NUM ROWS
_ _
------------------------------ ---------- ----------
T 2 150000
BLEVEL 为2,说明索引分支层数为2,则索引高度为3,即ORACLE 找到索引的叶 子节点上的索引键值需要执行3 次I/O。下面验证一下:
SQL>select max(id),min(id) from t;
MAX(ID) MIN(ID)
---------- ----------
150000 1
SQL> set autotrace traceonly
SQL> select * from t where id=1 ;
可以看出,无论是查找索引中最小键值 1,还是查找最大键值 1833792,ORACLE 都执行了三次 I/O (consistent gets = 3) 。因 此 B*树索引是一个绝佳的通用索引机制,无论是大表还是小表都很
适用,随着底层表数据的增长,获取数据的性能几乎不会恶化。
2) 适用场合
非常适合数据重复度低的字段例如身份证号码手机号码 QQ 号等 字段,常用于主键、唯一约束,一般在在线交易的项目中用到的多些。 原理:一个键值对应一行 (rowid)
3) 优点
当没有索引的时候,oracle 只能全表扫描where qq=40354446 这 个条件那么这样是非常非常耗时的,当数据量很大的时候简直会让人 崩溃,那么有个 B-tree 索引我们就像翻书目录一样,直接定位rowid 立刻就找到了我们想要的数据,实质减少了 I/O 操作就提高速度, 它有一个显著特点查询性能与表中数据量无关,例如查 2 万行的数 据用了 3 consistent get, 当查询 1200 万行 的数据时才用了 4 consistent gets。
当我们的字段中使用了主键 or 唯一约束时,不用想直接可以用 B-tree 索引
4) 缺点
不适合键值重复率较高的字段上使用,例如第一章 1-500page 第 二章 501-1000page
5) 实验
alter system flush shared_pool; //清空共享池
alter system flush buffer_cache; //清空数据库缓冲区,都是为 了实验需要
1 创建 T1、T2 表
t1 表的 object_id 列的数据是没有重复值的,我们抽取了 10 行数 据就可以看出来了。
SQL> create table t1 as select object_id,object_name from dba_objects;
SQL > select count(*) from t1;
COUNT(*)
----------
9872
SQL > select * from t1 where rownum <= 10;
OBJECT_ID OBJECT_NAME
---------- -----------
20 ICOL$
44 I USER1
_
28 CON$
15 UNDO$
29 C_COBJ#
3 I_OBJ#
25 PROXY_ROLE_DATA$
39 I IND1
_
51 I CDEF2
_
26 I_PROXY_ROLE_DATA$_1
t2 表的 object_id 列我们是做了取余操作,值就只有 0,1 两种,因 此重复率较高,如此设置为了说明重复率对 B 树索引的影响
SQL > create table t2 as select mod(object_id,2) object_ID ,object_name from dba_objects;
SQL > select count(*) from t2;
COUNT(*)
----------
9873
SQL > select * from t2 where rownum <= 10;
OBJECT_ID OBJECT_NAME
---------- -----------
0 ICOL$
0 I USER1 _
0 CON$
1 UNDO$
1 C_COBJ#
1 I_OBJ#
1 PROXY_ROLE_DATA$
1 I IND1 _
1 I CDEF2 _
0 I_PROXY_ROLE_DATA$_1
SQL > create index t1_index on t1(object_id) ; 创建 B-tree 索
引,说明默认创建的都是 B-tree 索引
Index created.
SQL > create index t2_index on t2(object_ID) ; 创建 B-tree 索 引
Index created.
让我们看一下 t1 与 t2 的重复情况
SQL > select count(distinct(object_id)) from t1 ; 让我们看一 下 t1 与 t2 的重复情况, t1 没有重复值, t2 有很多 COUNT(DISTINCT(OBJECT_ID))
--------------------------
9872
SQL > select count(distinct(object_ID)) from t2; COUNT(DISTINCT(OBJECT_ID))
--------------------------
2
收集 2 个表统计信息
SQL> exec dbms_stats.gather_table_stats(user,'T1',method_opt=>'for all indexed columns size 2',cascade=>TRUE);
SQL> exec dbms_stats.gather_table_stats(user,'T2',method_opt=>'for all indexed columns size 2',cascade=>TRUE);
6) 参数详解
method_opt=>'for all indexed columns size 2' size_clause=integer 整型,范围 1~254 ,使用柱状图[ histogram analyze ]分析列数据的分布情况
cascade=>TRUE 收集表的统计信息的同时收集B-tree 索引的统计信 息
SYS@orcl> set autotrace traceonly (set autotrace off 关闭)
SQL> select * from t1 where object_id=1;
SQL > select * from t2 where object_ID=1; (select /*+full( t2)
*/ * from t2 where object_ID=1;hint 方式强制全表扫描)
为什么要用全表扫描而不用 B-tree 索引呢,这是因为oracle 基 于成本优化器 CBO 认为使用全表扫描要比使用 B-tree 索引性能更好更快,由于我们结果重复率很高,导致有 1892 次一致性读,从 cup 使用率上看也说明了B-tree 索引不适合键值重复率较高的列我们在 看一下强制使用 B-tree 索引时,效率是不是没有全表扫描高呢? SQL > select /*+index( t2 t2_index) */ * from t2 where
object_ID=1; hint 方式强制索引扫描
小结:从以上的测试我们可以了解到,B-tree 索引在什么情况下使用跟键值重复率高低有很大关系的,只能多测试分析执行计划后来决定.
索引组织表 (index organized table 简称 IOT) 是 ORACLE 提供 的一种特殊类型的表,它将数据和索引存储在一起,按照索引的结构 来组织和存储表中的数据。索引组织表的存储结构不是堆表,堆表中 数据的存储是无序的,即将记录无序地存放在数据段中,而索引组织 表中的数据是按照某个主键排序后存储的,然后再以 B 树的组织结构 存储在数据段中,所以,索引组织表在一个 B 树索引结构中存储表行 的全部内容。使用堆组织表时,必须为表和表主键上的索引分别留出 空间。而 IOT 不存在主键的空间开销,因为索引就是数据,数据就是索引,二者已经合二为一。
索引组织表为包含精确匹配和范围搜索的查询提供了对表中数据 的快速访问,这种访问是快速的、基于主键的,但是却以牺牲插入和 更新性能为代价。
索引组织表对于经常使用主键字段来实现查询的事务非常高效, 使用索引组织表,能缩短具有精确匹配和主键范围搜索的查询时间。 索引组织表的主键约束不能被删除、延期和禁止。使用 IOT 表,其索 引与数据合二为一的特殊结构会带来很多好处,如 IOT 节约了磁盘空间的占用,大幅降低 I/O,从而减少了访问缓冲区缓存,尽管从缓冲 区缓存获取数据比从硬盘读要快得多,但缓冲区缓存并不免费,而且 也绝对不是廉价的。每个缓冲区缓存获取都需要缓冲区缓存的多个 闩,而闩是串行化设备,会限制应用的扩展能力。
IOT 类似一个全是索引的表,表中的所有字段都放在索引上,在 数据插入以前其实就已经确定了其位置,所以不管插入的先后顺序, 它在那个物理上的那个位置与插入的先后顺序无关。这样在进行查询 的时候就可以少访问很多blocks,但是插入的时候,速度就比普通 的表要慢一些。
索引组织表以主键的顺序存储数据,因此插入、更新和删除数据 都可能造成一条记录的物理位置发生变化,这时通过 ROWID 中的 DATAFILE 和 BLOCK 的信息可能就无法正确定位到记录的物理位置。 当根据逻辑 ROWID 访问索引组织表时,首先会根据 DATAFILE 和 BLOCK 信息去找到相应的 BLOCK,检查数据是否在这个 BLOCK 中,如 果不在,就通过逻辑 ROWID 中的主键信息去通过索引扫描,找到这条记录。
1、完全由主键组成的表,经常通过主键访问的表;
2、数据按特定顺序物理存储,IOT 就是一种合适的结构;
3、经常在一个主键或唯一键上使用between 查询;
4、经常更新的表不适合 IOT,因为 oracle 需要不断维护索引,
而且由于字段多索引成本就大。
5、不经常使用主键访问表,也不要使用 IOT;
SYS@orcl> create table tiot (x int primary key,y number,z varchar2(20)) organization index;
SYS@orcl> select TABLE_NAME,IOT_TYPE from user_tables where table_name='TIOT';
如果向索引组织表中添加数据,oracle 会根据主键列对其进行排 序,然后再将数据写入磁盘,所以在使用主键列查询时,相比标准表 来说,索引组织表可以得到更好的读取性能。
注意:创建 IOT 时,必须要设定主键,否则报错,索引组织表实 际上将所有数据都放入了索引中。
创建 IOT 表的关键是如何使用 IOT 参数
organization index:说明这是索引组织表;
overflow:允许创建一个新段,如果 IOT 的行记录太大,则可以
存储到这个新段上,什么时候使用这个新段?需要考虑including 和 pctthreshold 二个参数;
including:行中从第一列直到 including 所指定的列的所有列数
据都存储在索引块上,其余列存储在溢出段上;
pctthreshold:如果行中的数据量超过了数据块大小的百分比,
行中其余的数据就要放入溢出段;
compress: 索引组织表可以用 compress 子句进行键压缩 ,在
organization index 之后加上 compress n 子名,n 指定压缩的列 数,默认是无穷大。例如,对于数据(1,2,3)、(1,2,4)、(1,2,5)、
(1,3,4)、(1,3,5)时,若使用 COMPRESS 2 则会将重复出现的(1,2)、 (1,3)进行压缩,若使用 COMPRESS 1 时,只对数据(1)进行压缩, 减少 I/O,增大 CPU。
SYS@orcl> create table tiot1 (x int primary key,y number,z varchar2(20)) organization index pctthreshold 10 including y overflow tablespace users;
该表的主键是x,从第二列 (包含第二列) 开始的所有列数据都 存储到溢出段中,而对于第一列的数据只要不违反 pctthreshold 的 值就存储在索引块上,而一旦违反了这个规则就存储到溢出段中。
overflow 子句(行溢出),因为所有数据都放入索引,所以当表的 数据量很大时,会降低索引组织表的查询性能。此时设置溢出段将主 键和溢出数据分开来存储以提高效率。
溢出段的设置有两种格式:
pctthresholdn :制定一个数据块的百分比,当行数据占用大小 超出时,该行的其他列数据放入溢出段
including column_name :指定列之前的列都放入索引块,之后 的列都放到溢出段。
当行中某字段的数据量无法确定时使用pctthreshold。若所有行
均超出 pctthreshold 规定大小,则考虑使用 including。
唯一索引是 B 树索引的一种形式,当创建 B 树索引时,可以定义 它是唯一索引。在这方面,它就像唯一键约束。当插入数据到相应的 表时,唯一索引将保证插入到表中的非空值都是不同的。出于这个原 因,唯一索引通常与主键和唯一键约束联合使用。
唯一索引 unique index 和一般索引 normal index 最大的差异是
在索引列上增加一层唯一约束。
创建主键和唯一键约束的时候会自动创建一个与之对应的唯一索 引,名称与主键名称相同。如果不特别指定,这个索引的表空间和表 的表空间是一样的,但是不建议将两者放在一起。
create table test(name varchar(10));
alter table test add primary key(name) tablespace tablespace1;
以上方式有两个不好的地方,第一 :无法指定索引的名称;第二: 无法指定索引存放的表空间。
create table test_uid(name varchar(10),constraint test_uid_pk primary key(name) using index (create unique index uid_test_uid on test_uid(name) tablespace users)) ;
以上方式,指定索引的名称和指定索引存放的表空间。
create table testone (name varchar(10 char)) tablespace1;
alter table testone add constraint pk_testine1 primary key(name) using index tablespace tablespace2;
作为一个好习惯,不要把索引和表的数据放在同一个表空间。一 般索引单独建一个表空间。
通过 create unique index 语句指定创建唯一索引。 create unique index ind1 on testone(name) ;
反向键索引对于平衡有大量顺序插入的索引的 I/O 是非常有用 的。在需要一种方式均匀地分布索引数据,以避免将相似的值聚集在 一起时,这些索引表现更好。因此,当插入大量顺序值时,如果使用 反向键索引,就可以避免 I/O 集中在索引内的某个物理磁盘位置。反 向键索引通过 REVERSE 子句指定。注意 不能对位图索引或索引组织 表指定 REVERSE 子句。另外,反向键索引不能是降序类型的。
反向键索引的索引键的字节是反转存储的,例如:103 存储为 301。 通过反转方式,本来连续的数据就变得相距甚远,对这些索引键值的 插入就会分散到多个块上,从而有效降低大批量数据插入时,对同一 索引块的“争用”(buffer busy waits),避免“热块”的产生。
数据表进行海量数据插入操作时,尤其是在 RAC 环境下,这些本 来应该放在一个索引数据块上的连续值(一般是由序列或者时间戳产 生) 会被分散到不同的索引块上,从而避免索引块争用的问题。除了 反向索引,也可以使用 HASH 索引分区的方法解决上述争用问题。
反向索引的缺点:由于索引块不是连续顺序的,所以不能有效用 于索引列的范围查询 (如:BETWEEN, <, >, <=, >=, LIKE 等)。
默认情况下,Oracle 用升序方式存储 B 树索引。例如,如果在一 个列值为数值型数据的列上创建索引,最小的数值将首先出现在索引 (最左边的叶节点)中,而最大的数值将被储存在最右边的叶节点上。 通过对一列指定 DESC 关键字可以指示 Oracle 反转这种顺序为降序。 降序索引对于某些列以升序排序而另一些列以降序排序的查询是有用的。
位图索引是决策支持系统(Decision Support System,DSS)和数 据仓库的理想选择,在数据量非常大的表上的基数(Cardinality)(不 同值的数量)不高的列上建立位图索引,可以实现对这类表的快速访 问。对于在 WHERE 子句中使用多个 AND 或 OR 连接操作的SQL 语句(在 数据仓库环境中,这是典型的查询) ,位图索引也是高效的。
不建议在一些 OLTP(Online Transaction Processing,联机事务 处理)应用程序中使用位图索引。B 树索引中,一个 ROWID 对应一个 索引值中,如果更新表和上面的索引时,Oracle 可以锁定单独的行。 位图索引的索引值使用压缩格式存储,其中一个索引值会包含一系列 的 ROWID,因此 Oracle 不得不在更新一个给定值时锁定与其对应的 所有 ROWID。这种锁定类型可能在某些 DML 语句中造成死锁。SELECT 语句不会受到这种锁定问题的影响。不要在高负载的OLTP 环境中使 用位图索引。
位图索引有下面这些限制:
*基于规则的优化器不会考虑位图索引。
*当执行 ALTER TABLE 语句并修改包含有位图索引的列时,会
使位图索引失效。
*位图索引不包含任何列数据,并且不能用于任何类型的完整性
检查。
*位图索引不能被声明为唯一索引。
*位图索引最多包含 30 列。
使用关键字 BITMAP 创建位图索引。位图索引和位图连接索引只在 Oracle 企业版数据库中提供。
create bitmap index dept_idx2_bm on dept (deptno) ;
位图索引使用位图标识被索引的列值,它适合于没有大量更新任 务的数据仓库,因为使用位图索引时,每个位图索引项与表中大量的 行有关联,当表中有大量数据更新、删除和插入时,位图索引相应地 需要做大量更改,而且索引所占用的磁盘空间也会明显增加,并且索 引在更新时,受影响的索引需要锁定,所以位图索引不适合于有大量 更新操作的 OLTP 系统。
适用场合
列的基数很少,可枚举,重复值很多,数据不会被经常更新
优点:
OLAP 例如报表类数据库重复率高的数据特定类型的查询例如 count、or、and 等逻辑操作因为只需要进行位运算即可得到我们需 要的结果
缺点:
不适合重复率低的字段,还有经常 DML 操作 (insert,update, delete) ,因为位图索引的锁代价极高,修改一个位图索引段影响整 个位图段,例如修改一个键值,会影响同键值的多行,所以对于 OLTP 系统位图索引基本上是不适用的
实验
位图索引和 B-tree 索引的性能比较
set pagesize 100; 设置页大小
利用 dba_objects 数据字典创建一个 20 万行的表
SQL> create table leo_bm_t1 as select * from dba_objects; Table created.
SQL> insert into leo_bm_t1 select * from leo_bm_t1; 50364 rows created.
SQL> set autotrace off;
SQL> insert into leo_bm_t1 select * from leo_bm_t1; 100728 rows created.
SQL> select count(*) from leo_bm_t1;
COUNT(*)
----------
201456
因 object_type 字段重复值较高,故在此字段上创建 bitmap 索引 SQL>create bitmap index leo_bm_t1_index on leo_bm_t1(object_type) ;
Index created.
创建 一个和 leo_bm_t1 表 结构 一模 一样 的表 leo_bm_t2 , 并在 object_type 列上创建一个 B-tree 索引 (20 万行记录)
SQL> create table leo_bm_t2 as select * from leo_bm_t1; Table created.
SQL > create index leo_bm_t2_bt_index on
leo_bm_t2(object_type) ;
Index created.
对比位图索引和 B-tree 索引所占空间大小,很明显位图要远远小于 B-tree 索引所占用的空间,节约空间特性也是我们选择位图的理由 之一
SQL> col segment_name for a20
SQL > select segment_name,bytes from user_segments where segment_type='INDEX' and segment_name in ('LEO_BM_T1_INDEX','LEO_BM_T2_BT_INDEX') ;
SEGMENT NAME BYTES
_
-------------------- ----------
LEO BM T1 INDEX 131072
_ _ _
LEO BM T2 BT INDEX 5242880
_ _ _ _
显示执行计划和统计信息
set autotrace trace exp stat ;
在创建有位图索引的表上做 count 操作对比执行计划
SQL> select count(*) from leo_bm_t1 where object_type='TABLE' ;
SQL >select count(*) from leo_bm_t2 where object_type='TABLE' ;
查看位图索引:
SYS@orcl> col index_name for a25
SYS@orcl> col index_type for a15
SYS@orcl> col table_name for a10
SYS@orcl>select index_name,index_type,table_name,status from user_indexes where index_name like 'LEO%';
在 B 树索引中,可以实现行级锁定,但在位图索引中,由于对 ROWID 进行压缩存放,因此每次锁定的都是整个 ROWID 范围,因此对表中位 图索引列进行更新的时候,并发性很差,容易导致死锁,select语句不会受到这种锁定问题的影响。
基于函数的索引用其定义中的 SQL 函数或表达式创建。基于函数 的索引允许在被查询的 WHERE 子句中 SQL 函数引用的列使用索引查 找。基于函数类型的索引是必要的,因为查询引用一个应用了 SQL 函 数的列时,Oracle 将无法使用正常的 B 树索引。注意 基于函数的索 引可以为 B 树索引、唯一索引或位图索引。
在用户查询数据时,如果查询语句的 where 子句中有函数存在, oracle 使用函数索引将加快查询速度,基于函数的索引使用表中列 的函数值作为键值建立索引结构。创建基于upper 函数折函数索引
使用 ALTER INDEX 语句中的 REBUILD 选项,可以使用已有索引而 不是整个表快速重建索引:
alter index cust_idx1 rebuild parallel
tablespace cust_tblspc1;
利用 ALTER INDEX 语句中的 REBUILD 选项,可以使用已有索引而 不是表来快速重建索引。在执行这个操作时必须有足够的空间来保存 所有的索引。
重建索引并迁移其表空间
SCOTT@orcl> alter index pk_emp rebuild tablespace users ; 合并索引碎片,通过合并索引碎片可以释放部分磁盘空间
SCOTT@orcl> alter index pk_emp coalesce;
1、主键列和唯一键列的索引
在大多数情况下,应该为每个表创建主键约束。如果没有为主键 列定义索引,那么Oracle 会自动创建一个 B 树索引。
同样,对于在表上定义的任何唯一键约束,如果唯一键列上没有 已定义的索引,Oracle 也将创建一个合适的 B 树索引。
2、外键列的索引
Oracle 不会自动创建外键列的索引。建议在外键列上创建 B 树索 引的原因之一是,外键列经常在 WHERE 子句中被引用,并因此可以改 善这些查询的性能。
当外键列上存在索引时,可以避免或减少锁定问题。也就是说, 当插入或删除子表中的记录时,将在父表上放置一个表级别的锁,该 锁将阻止其他进程在父表中插入或删除记录。在 OLTP 数据库中,当
有多个进程同时插入和删除父表和子表中的记录时,这可能会成问 题。在数据仓库环境中,这个问题的影响更小,因为数据以更加系统 化的方式 (调度批处理作业) 被加载并且数据通常不会被删除。
3、其他适合创建索引的列
根据查询表时使用的列制定索引策略。既可以在一个表上创建多 个索引,也可以创建一个包含多个列的索引。如果事先考虑好需要在 表上执行什么类型的查询,那么就会做出更好的决策。如果你已经确 定了性能较差的 SQL 查询,也可以考虑为符合以下条件的列创建索 引。
为经常用作 WHERE 子句中谓词的列创建索引,如果在 WHERE 子句
中使用表的多个列,可以考虑使用组合 (多列) 索引。
为在 SELECT 子句中使用的列创建索引。
考虑为在 ORDER BY、GROUP BY、UNION 或者 DISTINCT 子句中使用
的列创建索引。
Oracle 允许创建包含多个列的索引。多列索引被称为组合索引(有
时也被称为复合索引) 。如果访问表时经常在 WHERE 子句中使用 多个列,那么这些索引特别有效。在这种情况下,组合索引常常 比分别创建多个单列索引更有效。
每个表上有多个索引没有关系。但是,在一个表上建立的索引越
多,DML 语句就会运行得越慢 (因为表列值变化时,Oracle 有越 来越多的索引需要维护) 。