Oracle 查询优化器 -- 访问路径

来源:互联网 发布:遇见未知的自己 知乎 编辑:程序博客网 时间:2024/05/18 04:54

-- Start

全表扫描(full table scans)

不要以为全表扫描就一定慢, 全表扫描时, 由于数据块在磁盘中是连续的,Oracle 可以一次读取多个块来提高查询效率,至于多少个块,是由 DB_FILE_MULTIBLOCK_READ_COUNT 决定的。所以,如果你需要返回一个表的大部分数据,全表扫描要比索引扫描快。除此之外,Oracle 会自动对小表进行全表扫描,那什么是小表呢?就是语句块小于 DB_FILE_MULTIBLOCK_READ_COUNT 定义的值。
如果你想让 Oracle 使用全表扫描,你也可以通过下面的方式指示 Oracle 使用全表扫描。
select /* FULL(t)*/ * from test t where col = 'test';

索引扫描(index scans)

你有没有想过,我们通过索引扫描时,如何通过索引定位到表中的位置呢?答案是通过 Rowid, Rowid 是 Oracle 内部使用的,用来标示行存储地址,所以通过 Rowid 定位行记录是最快的。有一点特别需要注意,Oracle 读写磁盘的最小单位是块,一个块可能包含多行,所以全表扫描还是索引扫描取决于访问块的百分比,而不是行的百分比。假设我们现在需要访问 3 行,这 3 行可能在一个块中,也可能在两个块中,还可能在三个块中,很明显,最理想的情况是在一个块中,Oracle 使用索引聚簇因子(index clustering factor)来衡量这种特性。索引聚簇因子越高,表明 Oracle 通过 Rowid 访问行的代价就越高。此外,Oracle 会根据索引类型的不同,是否排序,采用不同的索引扫描方式。

唯一索引扫描(Index Unique Scans)

如果你的查询条件有等价操作符(=),且恰好能用到唯一索引,那么 Oracle 会采用唯一索引扫描,当然你也可以通过下面的方式建议 Oracle 采用哪个索引。


select /* INDEX(t test_id_pk)*/ * from test t where col = 'test';

索引范围扫描(Index Range Scans)

如果你的查询条件有范围操作符(>,>=,<,<=,like 'abc%'),且恰好能用到索引,那么 Oracle 会采用索引范围扫描,当然你也可以通过下面的方式建议 Oracle 采用哪个索引。

select /* INDEX(t test_id_pk)*/ * from test t where col = 'test';

索引降序范围扫描(Index Range Scans Descending)

如果你的查询条件有范围操作符且要求用索引列排序,那么 Oracle 会采用索引降序范围扫描,这样做的好处是 Oracle 可以省略排序这个非常耗时的步骤。当然你也可以通过下面的方式建议 Oracle 采用索引降序范围扫描。

如果你的索引是升序的。select /* INDEX_DESC(t test_id_pk)*/ * from test t where col = 'test';如果你的索引是降序的。select /* INDEX_ASC(t test_id_pk)*/ * from test t where col = 'test';

索引跳跃扫描(Index Skip Scans)

如果你的索引是复合索引,也就是索引包含多个列,如下所示。

CREATE INDEX cust_idx ON customers (gender, email);

而你的查询语句没有用到索引的第一列 gender,只用到了第二列 email, 如下所示。


SELECT * FROM customers WHERE email = 'wave0409@163.com';

由于性别列(gender)只有男女两个值,Oracle 会采用如下的方式做查询。


SELECT * FROM customers WHERE gender='M' AND email = 'wave0409@163.com';UNION ALLSELECT * FROM customers WHERE gender='F' AND email = 'wave0409@163.com';

Oracle 把这种查询方式叫做索引跳跃扫描,需要注意的是这种方式只适合像性别这样,值非常少的列,你也可以通过下面的方式建议 Oracle 采用索引跳跃扫描。


select /* INDEX_SS(t test_id_idx)*/ * from test t where col = 'test';select /* INDEX_SS_ASC(t test_id_idx)*/ * from test t where col = 'test';select /* INDEX_SS_DESC(t test_id_idx)*/ * from test t where col = 'test';

全索引扫描(Full Scans)

如果你的查询需要排序或分组,且排序或分组用到的列恰好是索引列,那么 Oracle 会采用全索引扫描,由于索引列是有序的,这样 Oracle 可以省略排序这个非常耗时的步骤。


快速全索引扫描(Fast Full Index Scans)

如果你要查询的所有列都包含在索引中,Oracle 就不需要访问表了,这样 Oracle 就可能通过并行和一次读取多个块来提高查询索引的效率。你也可以通过下面的方式建议 Oracle 采用快速全索引扫描。

select /* INDEX_FFS(t test_id_pk)*/ * from test t where col = 'test';

索引连接扫描(Index Joins)

如果你的表有多个索引,恰好你要查询的所有列包含在这些索引中,Oracle 就不需要访问表了,Oracle 只需要把这些索引连接起来。你也可以通过下面的方式建议 Oracle 采用索引连接。

select /* INDEX_JOIN(t test_id_idx test_name_idx)*/ * from test t where col = 'test';

位图索引扫描(Bitmap Indexes)

如果你的表定义了位图索引,Oracle 会使用位图索引来做查询,你也可以通过下面的方式建议 Oracle 采用位图索引扫描。

select /* INDEX_COMBINE(t test_idx1 test_idx2)*/ * from test t where col = 'test';

索引聚簇扫描(Indexed Cluster Access)

如果你的表包含在了某个索引聚簇中,Oracle 会使用索引聚簇来执行查询。

Hash 聚簇扫描(Hash Cluster Access)

如果你的表包含在了某个Hash 聚簇中,Oracle 会使用 Hash 聚簇来执行查询。

采样扫描(Sample Table Scans)

如果你的查询语句包含 SAMPLE 子句,那么 Oracle 会使用 采样扫描。

--更多参见:Oracle SQL 优化精萃

-- 声明:转载请注明出处

-- Last edited on 2015-08-28

-- Created by ShangBo on 2015-07-17

-- End


0 0
原创粉丝点击