带你认识Oracle索引类型(精)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bless2015/article/details/83992007

Oracle索引和MySQL索引是一个概念,都是为了提高数据库查询效率,例如字典的目录,就是一种索引。不同的索引有不同的查询效率,比如字典的目录有以拼音首字母的,有偏旁部首的。当我们对所有索引类型有了了解之后,就可以针对性的写出高效的SQL语句、建立最合适的索引。
那Oracle索引都有哪些类型呢?
首先我们来看一下索引的数据结构:B树

在介绍之前,首先建立一张表

create table test(
	id number,
	a number,
	b number,
	c number
);
alter table test add constraint pk_test primary key (id) using index;

插入数据

begin
for i in 1 .. 100000 loop
	insert into test
	values
	(i,mod(i,2),mod(i,20000),mod(i,20000));
	end loop;
	commit;
end;

表建好了,有一个主键id,和三个值a、b、c。
下面开始我们的表演

INDEX UNIQUE SCAN索引唯一扫描

看下面这个SQL语句

select id from test where id = 5000;

我们知道id是主键,具有唯一性,而上述语句的SQL是一个等值条件(id=400,而<、>、>=等都属于非等值条件),所以该语句执行的索引为INDEX UNIQUE SCAN,这个速度是最快的。
条件:主键等值查询

INDEX RANGE SCAN索引范围扫描

看下面这个SQL

select id from test where id<5000;

这个属于主键上的非等值查询条件,走的就是索引范围扫描,当然还包括非主键索引情况。
下面我们再针对a、b、c建立两个索引:

create index idx_test_id_ab on test(a,b);
create index idx_test_id_c on test(c);

第一个索引是一个a和b的复合索引,第二个索引建立在c上。
我们看下面这个SQL

select * from test where c=200;

该语句也会走索引范围扫描。
你是否有疑问,我们如何知道语句是走哪种索引的?这可以从Oracle的执行计划看到,查看Oracle的执行计划只需要将你要观察的索引加入到其监控中。

exec dbms.stats.gather_table_stats('table_space','table_name');
exec dbms.stats.gather_index_stats('table_space','index_name');

条件:主键非等值查询、非主键查询。

INDEX FULL SCAN索引全扫描

对于表来说,有全表扫描。对于索引来说,也存在全索引扫描,与全表扫描非常类似。索引扫描只在CBO模式下起作用。

什么是CBO模式?这就不得不说Oracle的两种优化器:
RBO:Rule-Based Optimization,Oracle 10g版本后被弃用,RBO是基于预先设定好的语法优先级对语法进行执行计划的优化,所以开发者必须非常了解RBO的规则,这种方式非常呆板,因为其只认规则。
CBO:Cost-Based Optimization,Oracle 8引入,Oracle 10g取代了RBO,根据SQL执行情况的统计信息来对SQL的执行计划进行优化,这部分是Oracle公司保密的。问过Oracle公司的讲师说也不清楚具体细节。

这种方式有个特点,会自动对数据进行排序。省去了全表扫描后,再进行order by的操作。
因为B树索引本身就是排序好的,默认是ASC升序,可以在创建索引的时候进行指定。但是Oracle的执行计划会自动针对升序的降序查询进行优化,那么为什么要存在降序操作?答案是:在复合索引上,可以对(a desc,b asc),满足一定的业务场景。
我们看下面这个SQL

select * from test order by id;

因为排序的条件只有id,并且id已经建立索引,所以执行计划会被优化成INDEX FULL SCAN。
条件:表和表进行连接查询,查询语句中有order by,group by并且子句所有列都在索引中(联合索引)
小tip:如何看oracle的执行计划?sqlplus也可,但当然还有更好的方式,我用的Navicat,执行完sql后,可以通过它的“解释”功能看执行计划,走的什么索引类型,走的哪个索引。

INDEX FAST FULL SCAN快速全表扫描

快速全表扫描是扫描索引中的所有数据块,与全表扫描比,区别就是其不进行排序,即在这种方式下,返回的数据不是以排序的形式。可以多块读、并行读。所以叫FAST。
看下面的语句

select a,b from test where b<1000;

这个语句有两个特点,第一:返回值a和b都在索引上,第二:查询条件也在索引上。这条语句通过B树索引查询到rowid后,不需要额外在去原来的表里查数据了。为什么呢?回忆一下,符合索引包括根、枝、叶,叶子上存储的是索引值,包括:rowid、键值、键值长度、所属标号。看到没,如果所取的值都在索引上,就可以直接返回了,如果是

select a,b,c from test where b<1000;

这样返回值多了一个c,并不在复合索引上,所以还会用查到的rowid,去原表中取c的值,这样就不会走INDEX FAST FULL SCAN了。

INDEX SKIP SCAN索引跳跃扫描

这个也很简单,在复合索引中,可能会有如下类型数据:

a b
1 1
1 2
1 3
0 4
0 5
0 6

可以看b的值是不同的,a的取值只有0和1。索引跳跃扫描是扫描意思呢?就是当在这样一种a,b取值的情况下,对a和b建复合索引,oracle的优化器会将其优化成两个索引,分别是当a=0,a=1时的索引。
那么我们在a,b上建联合索引,仿佛有些问题。理论上不会在这样的一个没有多大区分度的a值上建索引的,所以一般看到INDEX SKIP SCAN,其一般开销都很大。

INDEX COMBINE索引组合扫描

当一个查询语句中,有两个查询,这两个查询列对应两个索引值。这种情况下就会出现索引组合扫描。比如:

select * from test where b<1000 and c>200;

ps,这里假设我们对b和c分别建立的索引。
走INDEX COMBINE会比单独走b或c的索引,开销都要小。
有心人是否看到,这个sql要取的值是所有,并非b或c。那么如果取的b或c呢?看下面的

INDEX JOIN索引联立

select b,c from test where b<1000 and c>200;

这种情况就是所查询的值在索引上,可以直接返回,不在用查到的rowid回原表取数据的情况。
严格来说,INDEX COMBINE和INDEX JOIN都不能算是一种独立的索引,只是对开头的5种索引的一种优化或补充。


到这里7种索引类型都介绍完了,留一个疑问

INDEX FAST FULL SCAN一定比INDEX FULL SCAN要快吗?

猜你喜欢

转载自blog.csdn.net/bless2015/article/details/83992007