2014-06-01 BaoXinjian
一、摘要
在PLSQL查询优化中,使用和接触最多的应该是索引Index这个概念,个人也觉得对Index选择和优化是程式优化过程中比较重要的概念,特别是刚开始接触PLSQL性能优化
索引的一些概念
- 一个索引可以由一个或多个列组成,
- 对列设置索引其实就是对列的内容按一定的方式进行排序,检索数据的时候,检索排过序的数据,检索到最后一个有效数据之后就跳出检索
- 这样就不必进行全表扫描了,同时可以应用很多算法提高检索效率
- 数据库多用二分法检索数据
索引的连接方式
- Hash Join
- Nested Loops
- Merge Join
索引的分类
- B-树索引
- 反向索引
- 降序索引
- 位图索引
- 函数索引
- 建立索引后需要分析索引才能是索引生效
- 主键和唯一性索引的区别
索引的中Hints使用
- 强制索引
建立索引的代价
- 基础表维护时,系统要同时维护索引,不合理的索引将严重影响系统资源,主要表现在CPU和I/O上;
- 插入、更新、删除数据产生大量db file sequential read锁等待;
有些情况下虽然在查询栏位中定义了index,但是PLSQL并没有走该栏位的索引,因为Oracle在解析计划时一种基于Role,一种是基于Cost有些情况下走Index的效率比全表扫秒更低,并不是建了Index,PLSQL性能会成倍提高,更糟的情况下,建Index还会导致性能比原来无索引更糟,这里有个理解误区,所以稍微提一下 ;)
而且,有时候你建立的索引可能应为空间或其他原因被失效后,所以会导致某些程式原本没有任何问题,突然出现性能问题,而且是非常大性能问题,所以对EBS DBA也有较高的要求,能监控一些系统异常
二、索引的连接方式
1. Hash Join
2. Nested Loops
3. Merge Join
具体解析
1. Hash Join
(1).概述
i. 读取一个表的资料,并将放置到内存中,并建立唯一关键字的位图索引
ii. 读取另一个表,和内存中表通过Hash算法进行比较
(2).适用对象
i. 大表连接小表
ii. 两个大表
2. Nested Loops
(1).概述
i. 循环外表记录
ii. 进行逐个比对和内标的连接是否符合条件
(2).适用对象
小表驱动大表,返回较少的结果集
3. Merge Join
(1).概述
i. 两个表进行table access full
ii. 对table access full的结果进行排序
iii. 进行merge join对排序结构进行合并
(2).适用对象
通过rowid访问数据
三、索引的分类
1. B-树索引
2. 反向索引
3. 降序索引
4. 位图索引
5. 函数索引
6. 建立索引后需要分析索引才能是索引生效
7. 主键和唯一性索引的区别
具体解析
1. B-树索引
(1).概述
最常用的索引结构,默认建立的索引就是这种结构
适用于高基数数据列(该列的值大多不一样)
(2).建立方式
CREATE INDEX index_name ON wip_entities (wip_entity);
2. 反向索引
(1).概述
(2).建立方式
3. 降序索引
(1).概述
适用于需要降序排列的列
(2).建立方式
CREATE INDEX index_name ON wip_entities (wip_entity DESC);
4. 位图索引
(1).概述
适用于低基数数据列(该列的值大多是一样)
(2).建立方式
CREATE BITMAP INDEX index_name ON wip_entities (sex);
5. 函数索引
(1).概述
适用于该列需要适用函数的列
(2).建立方式
CREATE INDEX index_name ON wip_entities (TRUNC(creation_date));
6. 分析索引
ANALYZE INDEX index_name COMPUTE STATISTICS;
7. 主键和唯一性索引的区别
(1). 主键是约束,唯一性索引只是一个索引
(2). 主键不可以为空,唯一性可以为空
四. 索引的中Hints使用 - 强制索引
1. 建立一个测试表,和测试索引
1 CREATE TABLE dba_name (
2
3 username VARCHAR(100),
4
5 password VARCHAR(100)
6
7 ) ;
8
9 CREATE INDEX index_t ON dba_name(username) ;
2. 方法比较
(1). 未使用强制索引
SELECT *
FROM dba_name
WHERE username = 'BAOXINJIAN'
(2). 使用过强制索引
SELECT /*+ index(t index_t) */
*
FROM dba_name t
WHERE username = 'BAOXINJIAN'
(3). 有些情况下虽然在查询栏位中定义了index,但是PLSQL并没有走该栏位的索引,因为Oracle在解析计划时一种基于Role,一种是基于Cost
有些情况下走Index的效率比全表扫秒更低,并不是建了Index,PLSQL性能会成倍提高,更糟的情况下,建Index还会导致性能比原来无索引更糟,这里有个理解误区,所以稍微提一下 ;)
五. 索引一些规则
1、权衡索引个数与DML之间关系,DML也就是插入、删除数据操作。
这里需要权衡一个问题,建立索引的目的是为了提高查询效率的,但建立的索引过多,会影响插入、删除数据的速度,因为我们修改的表数据,索引也要跟着修改。这里需要权衡我们的操作是查询多还是修改多。
2、把索引与对应的表放在不同的表空间。
当读取一个表时表与索引是同时进行的。如果表与索引和在一个表空间里就会产生资源竞争,放在两个表这空就可并行执行。
3、最好使用一样大小是块。
Oracle默认五块,读一次I/O,如果你定义6个块或10个块都需要读取两次I/O。最好是5的整数倍更能提高效率。
4、如果一个表很大,建立索引的时间很长,因为建立索引也会产生大量的redo信息,所以在创建索引时可以设置不产生或少产生redo信息。只要表数据存在,索引失败了大不了再建,所以可以不需要产生redo信息。
5、建索引的时候应该根据具体的业务SQL来创建,特别是where条件,还有where条件的顺序,尽量将过滤大范围的放在后面,因为SQL执行是从后往前的
六. 索引的操作
改变索引:
SQL> alter index employees_last _name_idx storage(next 400K maxextents 100);索引创建后,感觉不合理,也可以对其参数进行修改。详情查看相关文档
2. 调整索引的空间:
2.1 新增加空间
SQL> alter index orders_region_id_idx allocate extent (size 200K datafile '/disk6/index01.dbf');
2.2 释放空间
SQL> alter index oraers_id_idx deallocate unused;
索引在使用的过程中可能会出现空间不足或空间浪费的情况,这个时候需要新增或释放空间。上面两条命令完成新增与释放操作。关于空间的新增oracle可以自动帮助,如果了解数据库的情况下手动增加可以提高性能。
3. 重新创建索引:
所引是由oracle自动完成,当我们对数据库频繁的操作时,索引也会跟着进行修改,当我们在数据库中删除一条记录时,对应的索引中并没有把相应的索引只是做一个删除标记,但它依然占据着空间。除非一个块中所有的标记全被删除的时,整个块的空间才会被释放。这样时间久了,索引的性能就会下降。这个时候可以重新建立一个干净的索引来提高效率。
SQL> alter index orders_region_id_idx rebuild tablespace index02;通过上面的命令就可以重现建立一个索引,oracle重建立索引的过程:
1、锁表,锁表之后其他人就不能对表做任何操作。
2、创建新的(干净的)临时索引。
3、把老的索引删除掉
4、把新的索引重新命名为老索引的名字
5、对表进行解锁。
4. 移动所引:
其实,我们移动索引到其它表空间也同样使用上面的命令,在指定表空间时指定不同的表空间。新的索引创建在别位置,把老的干掉,就相当于移动了。
SQL> alter index orders_region_id_idx rebuild tablespace index03;
5. 在线重新创建索引:
上面介绍,在创建索引的时候,表是被锁定,不能被使用。对于一个大表,重新创建索引所需要的时间较长,为了满足用户对表操作的需求,就产生的这种在线重新创建索引。
SQL> alter index orders_id_idx rebuild online;创建过程:
1、锁住表
2、创建立临时的和空的索引和IOT表用来存在on-going DML。普通表存放的键值,IOT所引表直接存放的表中数据;on-gong DML也就是用户所做的一些增删改的操作。
3、对表进行解锁
4、从老的索引创建一个新的索引。
5、IOT表里存放的是on-going DML信息,IOT表的内容与新创建的索引合并。
6、锁住表
7、再次将IOT表的内容更新到新索引中,把老的索引干掉。
8、把新的索引重新命名为老索引的名字
9、对表进行解锁
6. 整合索引碎片:
如上图,在很多索引中有剩余的空间,可以通过一个命令把剩余空间整合到一起。
SQL> alter index orders_id_idx coalesce;
7. 删除索引:
SQL> drop index hr.departments_name_idx;
七. 索引的分析
检查所引的有效果,前面介绍,索引用的时间久了会产生大量的碎片、垃圾信息与浪费的剩余空间了。可以通过重新创建索引来提高所引的性能。
可以通过一条命令来完成分析索引,分析的结果会存放在在index_stats表中。
1. 查看存放分析数据的表:
SQL> select count(*) from index_stats;
COUNT(*)
----------
0
2. 执行分析索引命令:
SQL> analyze index my_bit_idx validate structure;
Index analyzed.
再次查看 index_stats 已经有了一条数据
SQL> select count(*) from index_stats;
COUNT(*)
----------
1
3. 把数据查询出来:
SQL> select height,name,lf_rows,lf_blks,del_lf_rows from index_stats;
HEIGHT NAME LF_ROWS LF_BLKS DEL_LF_ROWS
---------- ---------------------------------------------------------------------- ---------- -----------
2 MY_BIT_IDX 1000 3 100 分析数据分析:
4. 分析
- (HEIGHT)这个所引高度是2 ;
- (NAME)索引名为MY_BIT_IDX ;
- (LF_ROWS)所引表有1000行数据;
- (LF_BLKS)占用3个块;
- (DEL_LF_ROWS)删除100条记录’
- 这里也验证了前面所说的一个问题,删除的100条数据只是标记为删除,因为总的数据条数依然为1000条,占用3个块,那么每个块大于333条记录,只有删除的数据大于333条记录,这时一个块被清空,总的数据条数才会减少。
八. 索引的优点缺点
1. 索引的优点
2. 索引的缺点
九. 未走索引的情况
以下是使用索引不当所引起的不走索引的几种常见情况:
1. Index on t(x,y)但where 条件中只有y字段。
通常情况会进行全表扫描。
2. select count(*) from t通常由于索引比table小,oracle会进行index full scan。
但如果索引字段含有NULL值,则不会走索引,因为索引值不包含null,如果进行index full scan统计值就不准确了。
3. select * from t where f(index_column)=value
如果不是函数索引,where条件在索引字段上进行函数操作则不走索引
4. select * from t where indexed_column=5 字段类型需转换。
例如indexed_column是字符但where条件中用了数字
5. oracle优化器认为全表扫描比走索引效率更高。
这种情况下oracle选择全表扫描。如果开发人员觉得有必要走索引,可以使用hint强制走索引
6. 未及时对表进行analyze,statistics不准确。
例如原先是小表,后来数据量大增。由于statistics仍是旧的,oracle优化器会选择不走索引
********************作者: 鲍新建********************