访问索引的方法

首先要说明一点,以下提到是oracle数据库最常用的B树索引,oracle数据其他类型的索引暂不考虑,B树索引就好像一棵倒长的树,它包含两种类型的数据块,一种是索引分支块,另一种是索引叶子块。

索引分支块包含指向响应索引分支块/叶子块的指针和索引键值列(这里的指针是指相关分支块/叶子块的块地址RDBA,每个索引分支块都有两种类型的指针,一种是lmc,另一种是索引分支块的索引行记录的指针。lmc是left Most Child的缩写,每个索引分支块都只有一个lmc,这个Imc指向的分支块/叶子块中所有索引键值列中最大值一定下于该lmc所在索引分支块的索引索引键值列中的最小值;而索引分支块的索引行记录所记录的指针指向的分支块/叶子块的所有索引键值列的最小值一定大于或等于该行记录的索引键值列的值)在oracle里访问B数索引的操作都必须从根节点开始,即都会经历一个从根节点到分支块再到叶子块的过程。

索引叶子块包含被索引键值和用于定位该索引键值所在的数据行在表中的实际物理存储位置的ROWID。对于唯一性B树索引而言,rowid是存储在索引行的行头,所以此时并不需要额外存储该rowid的长度。对于非唯一性B数索引而言,rowid被当做额外的列与被索引的键值列一起存储,所以此时oracle既要存储rowid,同时又要存储其长度,这意味着在同等条件下,唯一性B树索引要比非唯一性B树索引节省索引叶子块的存储空间。对于非唯一索引而言,B树索引的有序性体现在oracle会按照被索引键值和相应的rowid来联合排序。oracle里的索引叶子块时左右互联的,即相当于有一个双向指针链表把这些索引叶子块互相连接了一起。

正是由于上述特点,oracle数据库中的B树索引才具有如下优势:

(1)所有的索引叶子块都在同一层,即他们距离索引根节点的深度是相同的,这也意味着访问索引叶子块的任何一个索引键值所花费的时间几乎相同。

(2)oracle会保证所有B树索引都是自平衡的,即不可能出现不同索引叶子块不处于同一层的现象。

(3)通过B树索引访问表的行记录的效率并不会随着相关表的数据量递增而显著降低,即通过走索引访问数据的时间是可控的,基本稳定的,这也是走索引和全表扫描的最大区别。全表扫描最大的劣势就在于其访问时间不可控,不稳定,即全表扫描花费的时间会随着目标表数据量的递增而递增。

B树索引的上述结构就决定了在oracle里通过B树索引访问数据的过程是先访问相关的B树索引,然后根据访问该索引后得到的rowid再回表去访问对应的数据行记录(当然,如果目标sql所访问的数据通过访问相关的B树索引可以得到,那么就不再需要回表了)。访问相关的B树索引和回表需要消耗I/O,这意味着在oracle中访问索引的成本由两部分组成:一部分是访问相关的B树索引的成本(从根节点到相关的分支块,再定位相关的叶子块,最后对这些叶子块执行扫描操作);另一部分是回表成本(根据得到的rowid再回表去扫描对应的数据行所在的数据块)

下面介绍oracle中一些常见的访问B树索引的方法

(1)索引唯一扫描

索引唯一性扫描(INDEX UNIQUE SCAN)是针对唯一性索引(UNIQUE INDEX)的扫描,它仅仅适用于where条件里是等值查询的目标sql。因为扫描的对象是唯一性索引,所以索引唯一性扫描的结果至多只会返回一条记录

(2)索引范围扫描

索引范围扫描(INDEX RANGE SCAN)适用于所有类型的B树索引,当扫描的对象是唯一性索引时,此时目标sql的where条件一定是范围查询(谓词条件为between,<,>);当扫描的对象是非唯一索引时,对目标sql的where条件没有限制(可以是等值查询,也可以时范围查询)。索引范围扫描的结果可能会返回多条记录,其实这就是索引范围扫描中范围"范围“二字的本质含义。

需要注意的是,即使是针对同条件下相同的sql,当目标索引的索引行的数量大于1时,索引范围扫描耗费的逻辑读会多于索引唯一扫描所耗费的逻辑读。这是因为所有唯一扫描结果至多只返回一条记录,所以oracle明确知道此时只需要访问相关的叶子块一次就可以直接返回了;但对于索引范围扫描而言,因为其扫描结果可能会返回多条记录,同时又因为索引的索引行数量大于1,oracle为了确定索引范围扫描的终点,就不得不去多访问一次相关的叶子块,所以在同等条件下,当目标索引的索引行的数量大于1时,索引范围扫描所耗费的逻辑读至少会比相应的索引唯一性的逻辑读多1.

实验验证:

SQL> create table emp_temp as select * from emp;

Table created.

现在emp_temp中列empno的非null值的数量为13(这意味着如果在表emp_temp的列empno上建单键值的B树索引,则该索引的索引行的数量一定大于1)

SQL> create unique index idx_emp_temp on emp_temp(empno);

Index created.

然后对表emp_temp和索引idx_emp_temp收集一下统计信息:

SQL> exec     dbms_stats.gather_table_stats(ownname=>‘SCOTT‘,tabname=>‘EMP_TEMP‘,estimate_percent=>10    0,cascade=>true,method_opt=>‘for all columns size 1‘);

PL/SQL procedure successfully completed.

为了避免buffer cache 和数据字典缓存(DATA Dictionary Cache)对逻辑读统计结果的影响。我们清空buffer cache和数据字典缓存

SQL> alter system flush shared_pool;

System altered.

SQL> alter system flush buffer_cache;

System altered.

执行计划:

SQL> select * from emp_temp where empno=7369;

Execution Plan

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

Plan hash value: 3451700904

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

| Id  | Operation    | Name   | Rows  | Bytes | Cost (%CPU)| Time   |

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

|   0 | SELECT STATEMENT    |   | 1 | 38 | 1   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEMP   | 1 | 38 | 1   (0)| 00:00:01 |

|*  2 |   INDEX UNIQUE SCAN    | IDX_EMP_TEMP | 1 |   | 0   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

2 - access("EMPNO"=7369)

Statistics

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

32  recursive calls

0  db block gets

67  consistent gets

13  physical reads

0  redo size

889  bytes sent via SQL*Net to client

512  bytes received via SQL*Net from client

1  SQL*Net roundtrips to/from client

6  sorts (memory)

0  sorts (disk)

1  rows processed

从以上显示可以看出,该sql的执行计划走的是索引唯一性扫描,其耗费的逻辑读是73.

现在我们drop掉唯一索引

SQL> drop index idx_emp_temp;

Index dropped.

然后在表emp_temp的列empno上创建一个单键值非唯一性同名B树索引idx_emp_temp:

SQL> create index idx_emp_temp on emp_temp(empno);

Index created.

收集统计信息:

SQL> exec     dbms_stats.gather_table_stats(ownname=>‘SCOTT‘,tabname=>‘EMP_TEMP‘,estimate_percent=>10    0,cascade=>true,method_opt=>‘for all columns size 1‘);

PL/SQL procedure successfully completed.

SQL> set autot traceonly

SQL> alter system flush shared_pool;

System altered.

SQL> alter system flush buffer_cache;

System altered.

SQL> select * from emp_temp where empno=7369;

Execution Plan

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

Plan hash value: 351331621

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

| Id  | Operation    | Name   | Rows  | Bytes | Cost (%CPU)| Time       |

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

|   0 | SELECT STATEMENT    |   | 1 | 38 | 2   (0)| 00:00:01     |

|   1 |  TABLE ACCESS BY INDEX ROWID| EMP_TEMP   | 1 | 38 | 2   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN    | IDX_EMP_TEMP | 1 |   | 1   (0)| 00:00:01     |

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

Predicate Information (identified by operation id):

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

2 - access("EMPNO"=7369)

Statistics

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

32  recursive calls

0  db block gets

68  consistent gets

16  physical reads

0  redo size

1025  bytes sent via SQL*Net to client

523  bytes received via SQL*Net from client

2  SQL*Net roundtrips to/from client

6  sorts (memory)

0  sorts (disk)

1  rows processed

上述结果看出,此sql的执行计划已经从之前的索引唯一扫描变成现在的索引范围扫描,其耗费的逻辑读也从之前的73递增到74,这说明在同等的条件下,当目标索引的索引行的数量大于1时,索引范围扫描所耗费的逻辑读确实知识会比相应的的索引唯一扫描多1.

时间: 2024-10-25 04:40:55

访问索引的方法的相关文章

ORACLE访问数据的方法

这篇是整理复习oracle关于访问表数据的方法,在oracle数据库中,要想访问存储在数据库中的数据, 要依次经历下面几个步骤: 待执行的SQL ----> 解析 ----> 优化器处理 ----> 生成执行计划 ----> 实际执行 ----> 返回执行结果, 其中,在优化器的处理这个阶段,来决定访问目标表数据的方式,即优化器要采用什么方式去访问具体 的数据. 在oracle中访问表的方式分为两种,一种是直接访问表,一种是先访问索引,再回表(当然,还有可能 只访问索引就可以

访问数据的方法

对于优化器而言,它在解析目标SQL,得到执行计划时至关重要的一点是决定访问数据的方法,即优化器要决定采用什么样的方式和方法去访问目标SQL所需要访问的存储在oracle数据库中的数据. 目标SQL所需要访问的数据一般存储在表里,而oracle访问表中数据的方法有两种:一种直接访问表,另一种是先访问索引,在回表(当然,如果目标sql所要访问的数据只通过访问相关的索引就可以得到,那么此时就不需要回表了) oracle数据库中直接方位表中数据的方法有两种:一是全表扫描,而是rowid扫描 1. 全表扫

C# 索引器方法

使用索引操作 [] 访问包含在一个标准数组中的各个子项. 定义:把能使用索引操作符 [] 访问子项的方法称为索引器方法 1.自定义索引器方法(this): public class PeopleCollection : IEnumerable { private ArrayList arPeople= new ArrayList(); // 类的自定义索引器 public Person this [int index] // 除了多个this关键字外,和属性声明很相似 { get{ return

mongoose中给字段添加索引的方法

mongoose中给字段添加索引的方法有两种,一种通过在定义schema的时候配置,如: 1 var animalSchema = new Schema({ 2 name: String, 3 type: String, 4 tags: { type: [String], index: true } 另一种通过index方法添加索引,如给name和type字段添加索引(1和-1分别表示升序索引和降序索引): animalSchema.index({ name: 1, type: -1 });

给大家分享web开发新手修改hosts文件实现本地域名访问的正确方法

1.如何正确修改hosts文件: 一般打开hosts文件里面都会有个示例,按照其格式修改即可 比如以下内容: # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host 即代表打开rhino.acme.com这个网址将解析到102.54.94.97,ip地址与网址间至少有一空格,当然建议通过按Table键来编辑,即美观又不容易编写失误;这也就是通过解

Python类属性访问的魔法方法

Python类属性访问的魔法方法: 1. __getattr__(self, name)- 定义当用户试图获取一个不存在的属性时的行为 2. __getattribute__(self, name)- 定义当该类的属性被访问时的行为 注意:当__getattr__与__getattribute__同时重写时,访问属性时,优先调用__getattribute__,只有当被访问的属性不存在时才触发__getattr__ 3. __setattr__(self, name, value)- 定义当一个

[转]eclipse启动tomcat无法访问的解决方法

这篇文章介绍了eclipse启动tomcat无法访问的解决方法,有需要的朋友可以参考一下 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://localhost:8080/不能访问,且报404错误.同时其他项目页面也不能访问. 关闭eclipse里面的tomcat,在tomcat安装目录下双击startup.bat手动启动tomcat服务器.访问htt://localhost:8080/能正常访问tomcat管理页面. 症状原因: eclipse将tomcat的项目

google无法访问 2014解决方法详解

方法二 寻找可用IP地址 IP地址一:http://74.125.205.147/ 即可访问谷歌 转:http://www.newasp.net/tech/89292.html 以是我个人实测的:配合 Chrome 浏览器.GoaAgent v3.1.8.SwitchySharp.crx.SwitchyOptions.bak 就可以访问google 而且搜索打开的页面很快. google无法访问 2014解决方法详解,布布扣,bubuko.com

IIS开启伪静态后html静态页面无法访问的解决方法

IIS开启伪静态后,发现原本存在的html静态页面无法访问了,显示的404错误.网上查了下,是因为实现伪静态就是使用 URLRewriter 来映射后缀,会把asp等动态页面映射成html,但是原来存在的html静态页面也被定义的规则映射到其它页面了,所以找不到页面而无法访问. 有一个很简单的方法就是将html页面的后缀名改成htm,或者是把映射的规则改成映射为htm,总之就是把我们的伪静态页面和真正的静态页面分别用html和htm区分开来,但这只是治标不治本的方法. 最好的方法就是修改伪静态规