oracle优化模型中5种索引访问方法的分析

本文主要讨论了以下几种索引访问方法:

1。索引唯一扫描(索引唯一扫描)

2。指数范围扫描(指数范围扫描)

三.索引完全扫描(索引完全扫描)

4。索引跳跃扫描(索引跳过扫描)

5。指数快速全扫描(指数快速全扫描)

索引唯一扫描(索引唯一扫描)

该指标的特点来访问数据的一个特定的值只返回一行数据时,通常使用独一无二的主键索引,在查询谓词中的列作为条件会选择这种扫描;获得高度的指标加上一个高度,除了一些特殊情况,如LOB对象存储。

复制代码代码如下所示:

SQL > AutoTrace traceonly解释

SQL SELECT * FROM hr.employees哪里employee_id > = 100;

执行计划

----------------------------------------------------------

计划哈希值:1833546154

---------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

---------------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 1 69 1(0)| 00:00:01 |

| 1表访问的索引rowid |员工| | | | 1 69 1(0)| 00:00:01 |

2索引唯一扫描| * | | emp_emp_id_pk | 1 | | 0(0)| 00:00:01 |

---------------------------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

2访问(employee_id= 100)



指数范围扫描(指数范围扫描)

谓词包含将返回一系列的数据条件,将选择范围索引,索引也只能是唯一的;指定的条件可以是(例如,在,= =)操作符之间,但是当使用时,如果使用通配符%,很可能使用扫描范围,因为条件太宽,下面是一个示例:

复制代码代码如下所示:

SQL SELECT * FROM hr.employees哪里department_id > = 30;

选择6行。

执行计划

----------------------------------------------------------

计划哈希值:2056577954

-------------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

-------------------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 6 414 2(0)| 00:00:01 |

| 1表访问的索引rowid |员工| | | | 6 414 2(0)| 00:00:01 |

2索引范围扫描| * | | emp_department_ix | 6 | | 1(0)| 00:00:01 |

-------------------------------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

2访问(department_id= 30)

统计

----------------------------------------------------------

8递归调用

0分贝块得到

7一致的获取

1物理读

0重做日志大小

1716个字节通过网络发送给客户机

从客户端接收的523个字节

2 SQL*Net切换到/从客户端

0类(内存)

0类(磁盘)

6行处理



范围扫描的范围要求对返回的数据数量进行精确分析,并且量程越大,执行全表扫描的可能性就越大。

复制代码代码如下所示:

SQL >选择department_id,计数(*)从department_id hr.employees组以计数(*);

department_id计数(*)



一百零一

四百零一



七百零一

二百零二

一千一百零二

九百零三

六百零五

三百零六

一千零六

八千零三十四

五千零四十五

选择12行。

它使用50个数值来执行范围扫描。

SQL > AutoTrace traceonly解释

SQL SELECT * FROM hr.employees哪里department_id > = 50;

选择45行。

执行计划

----------------------------------------------------------

计划哈希值:1445457117

-------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

-------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 45 3105 3(0)| 00:00:01 |

1表访问全|员工| * | | | | 45 3105 3(0)| 00:00:01 |

-------------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

1过滤器(department_id= 50)

统计

----------------------------------------------------------

0递归调用

0分贝块得到

10一致的获取

0物理读

0重做日志大小

4733个字节通过网络发送给客户机

从客户端接收的545个字节

4 SQL*Net切换到/从客户端

0类(内存)

0类(磁盘)

45行处理



您可以看到优化器在数据较大时仍然执行全表扫描。

索引范围扫描的一种优化方法是使用升序索引以降序的方式获取行。在索引列中包含子句的查询中,这种情况最为常见,因此可以避免一次操作。

复制代码代码如下所示:

SQL > AutoTrace traceonly解释

SQL SELECT * FROM hr.employees >

2 department_id在哪里(90, 100)

3阶的department_id desc;

执行计划

----------------------------------------------------------

计划哈希值:3707994525

---------------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

---------------------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 9 621 2(0)| 00:00:01 |

| 1 | InList迭代器| | | | | |

| 2表访问的索引rowid | |员工| | | 9 621 2(0)| 00:00:01 |

|×3索引范围扫描降| emp_department_ix | | 9 | | 1(0)| 00:00:01 |

---------------------------------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

3访问(department_id 或department_id = 90 = 100)



在本例中,索引条目的顺序相反,以避免读取、排序操作。

索引完全扫描(索引完全扫描)

索引完全扫描操作将扫描索引结构的每个叶块,读取每个项的行数并提取数据行。既然访问了每个索引叶块,那么它相对完整的表扫描的优势是什么呢事实上,因为包含在索引信息块少列,通常是键和rowid,所以对于同一块数据和索引项,索引键包含主要的索引块,所以如果从字段列表中的所有字段的查询的索引,可以完全跳过表数据访问方法的一部分,这个索引全扫描效率会更高。

有很多情况下,索引的全面扫描,几个典型的场景:

1,查询总是缺少谓词,但是得到的列可以直接通过索引获得。

复制代码代码如下所示:

SQL >选择电子邮件从hr.employees;

执行计划

----------------------------------------------------------

计划哈希值:2196514524

---------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

---------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 107 856 1(0)| 00:00:01 |

1索引全扫描| | | emp_email_uk | | | 107 856 1(0)| 00:00:01 |

---------------------------------------------------------------------------------



2,查询谓词包含索引中非引导列的条件。实际上,这取决于引导值的基值的大小。如果引导列的唯一值较小,则跳过扫描也是可能的。

复制代码代码如下所示:

已选择从first_name,last_name hr.employees

2在first_name like'a %;

执行计划

----------------------------------------------------------

计划哈希值:2228653197

--------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 3 45 1(0)| 00:00:01 |

1索引全扫描| * | | emp_name_ix | | | 3 45 1(0)| 00:00:01 |

--------------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

1访问(first_namelike'a %)

滤波器(first_namelike'a %)

SQL >设置长2000000

SQL >选择dbms_metadata.get_ddl('index ','emp_name_ix ','hr)从双;

dbms_metadata.get_ddl('index ','emp_name_ix ','hr)

--------------------------------------------------------------------------------

创建索引HR。emp_name_ix 人力资源。员工(last_name



10 255 2 maxtrans PCTFREE initrans nologging计算统计

STORAGE (INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645)

0 1 1 pctincrease freelists空闲列表组buffer_pool默认flash_cache de

故障cell_flash_cache默认)

表空间实例

你可以看到,emp_name_ix建立的索引列(last_name



3,通过排序索引获得数据,以保存单个排序操作。

复制代码代码如下所示:

SQL SELECT * FROM的employee_id秩序>> hr.employees;

执行计划

----------------------------------------------------------

计划哈希值:2186312383

---------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

---------------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 107 7383 3(0)| 00:00:01 |

| 1表访问的索引rowid |员工| | | | 107 7383 3(0)| 00:00:01 |

2索引全扫描| | | emp_emp_id_pk | 107 | | 1(0)| 00:00:01 |

---------------------------------------------------------------------------------------------

-还可以使用升序索引返回下行数据。

SQL >选择employee_id由employee_id倒序hr.employees;

执行计划

----------------------------------------------------------

计划哈希值:753568220

--------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 107 428 1(0)| 00:00:01 |

| 1全索引扫描降| emp_emp_id_pk | | | | 107 428 1(0)| 00:00:01 |

--------------------------------------------------------------------------------------------



在上面的例子中我们可以看到,全索引扫描也可以想扫描范围,通过升降指数收益率数据,并优化不仅仅是这一个,当我们查询列表中的最大值或最小值的列的列的索引时,索引全扫描会很重要优势,因为搜索优化并不是对所有叶节点的索引数据,但只有一根块,第一或最后一片叶子块扫描,这无疑将大大提高性能!!

复制代码代码如下所示:

-索引完全扫描以获得最小值

SQL >选择min(department_id)从hr.employees;

执行计划

----------------------------------------------------------

计划哈希值:613773769

------------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

------------------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 1 3 1(0)| 00:00:01 |

| 1 |排序总| | 1 | 3 | | |

| 2 |索引全扫描(最小/最大)emp_department_ix | | | | 1 3 1(0)| 00:00:01 |

------------------------------------------------------------------------------------------------

如果同时包含最大和最小的评估,优化器不主动选择有效的索引完全扫描方法。

SQL >选择min(department_id),最大(department_id)从hr.employees;

执行计划

----------------------------------------------------------

计划哈希值:1756381138

--------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 1 3 3(0)| 00:00:01 |

| 1 |排序总| | 1 | 3 | | |

2表访问全|员工| | | | | 107 321 3(0)| 00:00:01 |

--------------------------------------------------------------------------------

一种可供选择的优化方案

SQL >选择

2(选择min(department_id)来自人力资源。员工)min_id,

3(选择最大(department_id)来自人力资源。员工)max_id

4来自双;

执行计划

----------------------------------------------------------

计划哈希值:2189307159

------------------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

------------------------------------------------------------------------------------------------

| 0 | SELECT语句| | 1 | | 2(0)| 00:00:01 |

| 1 |排序总| | 1 | 3 | | |

| 2 |索引全扫描(最小/最大)emp_department_ix | | | | 1 3 1(0)| 00:00:01 |

| 3 |排序总| | 1 | 3 | | |

| 4 |索引全扫描(最小/最大)emp_department_ix | | | | 1 3 1(0)| 00:00:01 |

| 5 |快速双| | 1 | | 2(0)| 00:00:01 |

------------------------------------------------------------------------------------------------



索引跳跃扫描(索引跳过扫描)

这种扫描是一种特殊的情况,因为在较早的版本,优化器会拒绝使用索引,因为使用非引导柱。跳跃扫描的前提下有一个相应的场景,当非引导上市包含在谓词索引条件,引导小当柱,有可能使用索引跳跃扫描法;同一索引全扫描,扫描范围内,它也可以在升序或降序索引访问;不同的跳跃扫描根据列数将引导的唯一价值逻辑子指数分为更小,更小的子指标导柱只数。分割也较少,更有可能实现相对完整的表扫描,效率很高。

复制代码代码如下所示:

-创建一个测试表,以dba_objects表为例

SQL >创建测试表为SELECT * FROM dba_objects;

表的创建。

-创建一个复合索引,其中选择具有唯一值的单个所有者列作为引导列。

已创建的测试指标i_test(业主,object_id,object_type);

建立索引。

统计信息分析表的收集

SQL> exec dbms_stats.gather_table_stats('sys '测试');

已成功完成。

-看看引导列的唯一值的比较

从测试中选择计数(*)、计数(不同的所有者);

计数(*)计数(distinctowner)

这个

七百二十四万八千二百二十九

-访问触发跳跃扫描使用一个条件查询一个非启动柱

SQL >选择*从测试,object_id = 46;

执行计划

----------------------------------------------------------

计划哈希值:1001786056

--------------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------------------

| 0 | SELECT语句| | | | 1 97 31(0)| 00:00:01 |

| 1表访问的索引rowid |测试| | | | 1 97 31(0)| 00:00:01 |

2索引跳跃扫描| * | | i_test | 1 | | 30(0)| 00:00:01 |

--------------------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

2访问(object_id= 46)

滤波器(object_id= 46)

统计

----------------------------------------------------------

101递归调用

0分贝块得到

38一致的获取

0物理读

0重做日志大小

1610个字节通过网络发送给客户机

从客户端接收的523个字节

2 SQL*Net切换到/从客户端

3类(内存)

0类(磁盘)

1行处理

-看看这个声明的全部扫描效率

SQL >选择全(测试) / * * *测试,object_id = 46;

执行计划

----------------------------------------------------------

计划哈希值:1357081020

--------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------

| 0 | SELECT语句| | | | 1 97 282(1)| 00:00:04 |

1表访问全|测试| * | | | | 1 97 282(1)| 00:00:04 |

--------------------------------------------------------------------------

谓词信息(由操作ID标识):

离开

1过滤器(object_id= 46)

统计

----------------------------------------------------------

1递归调用

0分贝块得到

1037一致的获取

0物理读

0重做日志大小

1607个字节通过网络发送给客户机

从客户端接收的523个字节

2 SQL*Net切换到/从客户端

0类(内存)

0类(磁盘)

1行处理



上面的查询分析可以看出,我们使用索引向导列有29个唯一的值,也就是说当执行索引跳过扫描时,分为29个逻辑子索引查询,只产生38次1037次读逻辑;逻辑相对完整的表扫描读取性能非常明显!

指数快速全扫描(指数快速全扫描)

这种访问方法在数据采集和全表扫描是一样的,多块障碍阅读,所以它不能够使用它来避免订货成本;索引快速全扫描通常发生在查询中的列,索引和一列的索引有一个非空的约束,当然这条件也容易全索引扫描,它可以用在全表扫描,将数据采集不到数据块表。

复制代码代码如下所示:

-仍然使用上面创建的测试表

SQL> DESC测试

名称零式

-----------------------------------------------------------------------------

业主VARCHAR2(30)

object_name VARCHAR2(128)

subobject_name VARCHAR2(30)

object_id非空数

data_object_id数

object_type VARCHAR2(19)

创建日期

last_ddl_time日期

时间戳VARCHAR2(19)

现状VARCHAR2(7)

临时VARCHAR2(1)

生成的VARCHAR2(1)

二VARCHAR2(1)

NAMESPACE NUMBER

edition_name VARCHAR2(30)

-创建一个在object_id列索引

已创建的测试指标pri_inx(object_id);

建立索引。

-直接全表扫描

SQL >选择object_id测试;

选择72482行。

执行计划

----------------------------------------------------------

计划哈希值:1357081020

--------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------

| 0 | SELECT语句| | | 72482 353k | 282(1)| 00:00:04 |

1表访问全|测试| | | | 72482 353k | 282(1)| 00:00:04 |

--------------------------------------------------------------------------

统计

----------------------------------------------------------

1递归调用

0分贝块得到

5799一致的获取

0物理读

0重做日志大小

1323739个字节通过网络发送给客户机

从客户端接收的53675个字节

4834 SQL*Net切换到/从客户端

0类(内存)

0类(磁盘)

72482行处理

-修改object_id不空

SQL> ALTER TABLE测试修改(object_id不为空);

表的改变。

与object_id列的查询,你可以看到快速使用全扫描

SQL >选择object_id测试;

选择72482行。

执行计划

----------------------------------------------------------

计划哈希值:3806735285

--------------------------------------------------------------------------------

我的名字|操作| | |行| |字节成本(CPU)时间| |

--------------------------------------------------------------------------------

| 0 | SELECT语句| | | 72482 353k | 45(0)| 00:00:01 |

1索引快速全扫描| | | | | pri_inx 72482 353k | 45(0)| 00:00:01 |

--------------------------------------------------------------------------------

统计

----------------------------------------------------------

167递归调用

0分贝块得到

5020一致的获取

161物理读

0重做日志大小

1323739个字节通过网络发送给客户机

从客户端接收的53675个字节

4834 SQL*Net切换到/从客户端

4类(内存)

0类(磁盘)

72482行处理



ps,这个索引的例子,快速全扫描,真是模拟不好,上面的例子已经很长时间了。