索引:
最常用的索引包括:B*树索引、位图索引、位图联合索引、基于函数索引、应用域索引
1 B*书索引:实现快速定位,数据库会根据所索引的列来建立索引,如果该列是唯一的就将该列进行排序,然后建立索引,如果该列不唯一则加入rowid列,使其达到唯一,然后进行排序,借用oracle编程艺术中的图:
B*树索引包括如下几种:
普通索引
反向键索引:将索引列用reverse函数反序后建立索引,这是为了避免当同时插入数据太多,导致同一个索引块忙碌的情况,比如同时插入1w个记录,这1w个记录就会同时在一个索引块里创建索引,导致该块忙碌,影响性能
降序索引:索引默认是按照索引列递增的顺序进行排序的,如果索引列是递减的也可以用默认的索引,毕竟倒过来就是递减的;但是如果索引列有2个字段,而当我查询的时候一个是asc,一个是desc,这时候默认索引到不到预期的效果;但是如果给递减列用上降序索引,的增列用上默认索引的话,那么性能会大幅提高;
关于聚簇因子:oracle有一个表叫user_indexes,在这里可以查询所有的索引,比如执行查询
select a.index_name,b.num_rows,b.blocks,a.clustering_factor from user_indexes a, user_tables b
where index_name in ('xx1','xx2') and a.table_name = b.table_name;
其中clustering_factor:聚簇因子 index_name索引名 num_rows:行数 blocks:数据块
如果clustering_factor非常接近blocks,说明数据非常有序,连续的数据可能在一个块中,这样进行区间段等大数据查询的时候,性能越高
如果clustering_factor非常接近num_rows,说明数据非常随即。索引条目指向的数据不一定在一个快中,进行区间等查询的时候,性能很差
B*树索引应用场合:1 仅仅返回少数数据,数据重复度低;2 直接用索引能完成,没有涉及到表(比如select count(x) where x='xx',这种语句直接检索索引,根本接触不到表本身,速度显然很快)
2 位图索引:对于频繁更新的表,不适合建立位图索引;位图索引会记录该列的数据1存放在1,3,4,7...行,数据2存放在2,5,6...行,数据null存放在8,9...行等等,会把所有数据及其位置都记录下来;
所以,数据的重复度越高,越适合位图索引;如何衡量呢,就是不同相数据的个数除以总行数越接近零越好;伪代码:count(distinct(col))/count(*)越接近零,越适合做位图索引;
以上数据不应该建立B*树索引,因为B*树是树形结构,而重复度高的数据,会根据一个值检索到大量的数据。这违背了b*树索引的原则。
如果进行查询select count(*) from Table1 where col1='x' or col1='y',这个就会用到位图索引,只是将2个合并,都不会去查询表,在索引中直接返回正确结果
如果进行查询select * from table1 wwhere col1='x' or col1='y',这个会用到位图索引,oracle会到位图索引去找到所有x和y的索引,然后找到相应的rowid,然后根据rowid快速返回结果集
对于频繁更新的表,为什么不适合用位图索引:一个位图索引中维护了一个数据和多个行的位置,如果一个会话修改了该列某一行的指定数据,比如xx1,那么该位图索引所维护的xx1数据的所有行都会被锁定;oracle是无法锁定一个位图索引中的某一个行的。这样就会出现如下问题:
如果该数据有100行,那么更新一行的时候,100行数据均被锁定,导致其他会话只能等待期释放之后才能操作;如果频繁出现这种情况的话,性能会严重下降。
3 位图联结索引:这是oracle9i所引入的一个索引;对于关联表建立位图索引,比如有一个部门表和员工表,员工表中存放了部门表的主键,此时想根据部门的name来查询所有的员工,这是如果建立和位图联结索引,聚会很快得到结果集;
4 基于函数的索引(待续)
关于索引常见的问题:
1 视图能建立索引吗:视图引用的是基本表,只需要对基本表建立索引,就相当于在视图中建立索引;
2 Null和索引的协作:
-- B*树索引不能建立在都可以为null的列上,因为B*书索引会过滤掉null,也就是说,当进行查询的时候:
比如在col1和col2上建立了索引,col1和col2都可以为空,查询select * from table1 where col1 is null 这个语句不会使用索引,因为如果使用索引的话,可能会丢失数据(那些null的数据会丢失);因为索引无法建立在都可以为null的列上,oracle优化器会选择不使用索引,而是全表扫描;
3 外键是否可以建立索引:在以下情况中,最好在外键上建索引
a 如果经常进行联机删除或者修改父表主键的情况,建议在子表外键上建立索引。如果不建立索引的话,有上述操作时,oracle会把整个子表全部锁死,这也是oracle出现死锁的情况,严重影响系统性能;
b 从父表查询子表信息的时候,比如根据deptName查询该部门下所有员工信息的时候,建议在子表外键上建立索引,或者建立位图联结索引,否则如果频繁进行上述查询的话,速度会很慢,因为每次都对员工表进行了全表扫描。
4 索引为何失效
a 关于B*树索引,如果是a、b两列建立索引,如果a、b两列都允许为空的话,由于在全是null的数据上不会建立索引,所以为了避免查询数据不准确,优化器会放弃使用索引,而是使用全表扫描;
b 关于B*树索引,如果是a、b两列建立索引,如果这样select * from table1 where b =5;此时如果a列的数据重复率不高的话,如果用索引的话,会去检索每条索引的数据,这样优化器更倾向于用全表扫描;如果a数据重复度很高,优化器会用一种跳跃式扫描方式,此时会用上索引;
c 如果用到了函数,比如select * from table1 where f(a) = xx;此时不会用到索引,因为索引是建立在a上的,而不是建立在f(a)上的;
d 如果a列是字符类型,但是我们执行了select * from table1 where a = 5;此时其实是隐式的应用了to_number(a)=5;索引是建立在a上,而不是建立在函数上的;
e 有些时候oracle的优化器会智能的判断使用索引后是否会提高效率,如果不会提高效率那么优化器会自动放弃索引,而执行full table;此时请检查索引的合理性;
oracle编程艺术上说:归根结底,原因通常就是
“不能使用索引,使用索引会返回不正确的结果”
“不应该使用索引,使用了,性能会变得很糟糕”
另外:如果是两个列建立索引,那么哪个放前面,哪个放后面呢:
这个要视情况而定,如果只有如下查询select * from table1 where a= :x and b= :y,那么哪个在前面都无所谓;
如果还有这种方式:select * from table1 where b = :y ,那么需要b列在前面;
oracle编程艺术中还就索引压缩做出对比,这里不记录了。以上都是个人理解记录,都是为了方便自己。