oracle-索引原理

Oracle索引原理

Oracle提供了大量索引选项。知道在给定条件下使用哪个选项对于一个应用程序的性能来说非常重要。一个错误的选择可能会引发死锁,并导致数据库性能急剧下降或进程终止。而如果做出正确的选择,则可以合理使用资源,使那些已经运行了几个小时甚至几天的进程在几分钟得以完成,这样会使您立刻成为一位英雄。这篇文章就将简单的讨论每个索引选项。主要有以下内容:

[1] 基本的索引概念

查询DBA_INDEXES视图可得到表中所有索引的列表,注意只能通过USER_INDEXES的方法来检索模式(schema)的索引。访问USER_IND_COLUMNS视图可得到一个给定表中被索引的特定列。

[2] 组合索引

当某个索引包含有多个已索引的列时,称这个索引为组合(concatented)索引。在 Oracle9i引入跳跃式扫描的索引访问方法之前,查询只能在有限条件下使用该索引。比如:表emp有一个组合索引键,该索引包含了empno、 ename和deptno。在Oracle9i之前除非在where之句中对第一列(empno)指定一个值,否则就不能使用这个索引键进行一次范围扫描。

特别注意:在Oracle9i之前,只有在使用到索引的前导索引时才可以使用组合索引!

[3] ORACLE ROWID

通过每个行的ROWID,索引Oracle提供了访问单行数据的能力。ROWID其实就是直接指向单独行的线路图。如果想检查重复值或是其他对ROWID本身的引用,可以在任何表中使用和指定rowid列。

[4] 限制索引

限制索引是一些没有经验的开发人员经常犯的错误之一。在SQL中有很多陷阱会使一些索引无法使用。下面讨论一些常见的问题:

4.1 使用不等于操作符(<>、!=)

下面的查询即使在cust_rating列有一个索引,查询语句仍然执行一次全表扫描。

select cust_Id,cust_name

from   customers

where  cust_rating <> ‘aa‘;

把上面的语句改成如下的查询语句,这样,在采用基于规则的

优化器而不是基于代价的优化器(更智能)时,将会使用索引。

select cust_Id,cust_name

from   customers

where  cust_rating < ‘aa‘ or cust_rating > ‘aa‘;

特别注意:通过把不等于操作符改成OR条件,就可以使用索引,以避免全表扫描。

4.2 使用IS NULL 或IS NOT NULL

使用IS NULL 或IS NOT NULL同样会限制索引的使用。因为NULL值并没有被定义。在SQL语句中使用NULL会有很多的麻烦。因此建议开发人员在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引,关于位图索引在稍后在详细讨论)。

4.3 使用函数

如果不使用基于函数的索引,那么在SQL语句的WHERE子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。 下面的查询不会使用索引(只要它不是基于函数的索引)

select empno,ename,deptno

from   emp

where  trunc(hiredate)=‘01-MAY-81‘;

把上面的语句改成下面的语句,这样就可以通过索引进行查找。

select empno,ename,deptno

from   emp

where  hiredate<(to_date(‘01-MAY-81‘)+0.9999);

4.4 比较不匹配的数据类型

比较不匹配的数据类型也是比较难于发现的性能问题之一。

注意下面查询的例子,account_number是一个VARCHAR2类型,

在account_number字段上有索引。下面的语句将执行全表扫描。

select bank_name,address,city,state,zip

from   banks

where  account_number = 990354;

Oracle可以自动把where子句变成to_number(account_number)=990354,这样就限制了

索引的使用,改成下面的查询就可以使用索引:

select bank_name,address,city,state,zip

from   banks

where  account_number =‘990354‘;

特别注意:不匹配的数据类型之间比较会让Oracle自动限制索引的使用,

即便对这个查询执行Explain Plan也不能让您明白为什么做了一次“全表扫描”。

[5] 选择性

使用USER_INDEXES视图,该视图中显示了一个distinct_keys列。比较一下唯一键的数量和表中的行数,就可以判断索引的选择性。选择性越高,索引返回的数据就越少。

[6] 群集因子(Clustering Factor)

Clustering Factor位于USER_INDEXES视图中。该列反映了数据相对于已索引的列是否显得有序。如果Clustering Factor列的值接近于索引中的树叶块(leaf block)的数目,表中的数据就越有序。如果它的值接近于表中的行数,则表中的数据就不是很有序。

[7] 二元高度(Binary height)

索引的二元高度对把ROWID返回给用户进程时所要求的I/O量起到关键作用。在对一个索引进行分析后,可以通过查询DBA_INDEXES的B- level列查看它的二元高度。二元高度主要随着表的大小以及被索引的列中值的范围的狭窄程度而变化。索引上如果有大量被删除的行,它的二元高度也会增加。更新索引列也类似于删除操作,因为它增加了已删除键的数目。重建索引可能会降低二元高度。

[8] 快速全局扫描

在Oracle7.3后就可以使用快速全局扫描(Fast Full Scan)这个选项。这个选项允许Oracle执行一个全局索引扫描操作。快速全局扫描读取B-树索引上所有树叶块。初始化文件中的 DB_FILE_MULTIBLOCK_READ_COUNT参数可以控制同时被读取的块的数目。

[9] 跳跃式扫描

从Oracle9i开始,索引跳跃式扫描特性可以允许优化器使用组合索引,即便索引的前导列没有出现在WHERE子句中。索引跳跃式扫描比全索引扫描要快的多。下面的程序清单显示出性能的差别:

create index skip1 on emp5(job,empno);

index created.

select count(*)

from emp5

where empno=7900;

Elapsed:00:00:03.13

Execution Plan

0     SELECT STATEMENT Optimizer=CHOOSE(Cost=4 Card=1 Bytes=5)

1  0    SORT(AGGREGATE)

2  1      INDEX(FAST FULL SCAN) OF ‘SKIP1‘(NON-UNIQUE)

Statistics

6826 consistent gets

6819 physical   reads

select /*+ index(emp5 skip1)*/ count(*)

from emp5

where empno=7900;

Elapsed:00:00:00.56

Execution Plan

0     SELECT STATEMENT Optimizer=CHOOSE(Cost=6 Card=1 Bytes=5)

1  0    SORT(AGGREGATE)

2  1      INDEX(SKIP SCAN) OF ‘SKIP1‘(NON-UNIQUE)

Statistics

21 consistent gets

17 physical   reads

[10] 索引的类型

B-树索引

位图索引

HASH索引

索引编排表

反转键索引

基于函数的索引

分区索引

本地和全局索引

逻辑上:

Single column 单行索引

Concatenated 多行索引

Unique 唯一索引

NonUnique 非唯一索引

Function-based函数索引

Domain 域索引

物理上:

Partitioned 分区索引(全局索引和本地索引)

NonPartitioned 非分区索引

B-tree:

Normal 正常型B树

Rever Key 反转型B树

Bitmap 位图索引

索引结构:

B-tree:

适合与大量的增、删、改(OLTP);

不能用包含OR操作符的查询;

适合高基数的列(唯一值多)

典型的树状结构;

每个结点都是数据块;

大多都是物理上一层、两层或三层不定,逻辑上三层;

叶子块数据是排序的,从左向右递增;

在分支块和根块中放的是索引的范围;

Bitmap:

适合与决策支持系统;

做UPDATE代价非常高;

非常适合OR操作符的查询;

基数比较少的时候才能建位图索引;

树型结构:

索引头

开始ROWID,结束ROWID(先列出索引的最大范围)

BITMAP

每一个BIT对应着一个ROWID,它的值是1还是0,如果是1,表示着BIT对应的ROWID有值;

B*tree索引的话通常在访问小数据量的情况下比较适用,比如你访问不超过表中数据的5%,当然这只是个相对的比率,适用于一般的情况。bitmap的话在数据仓库中使用较多,用于低基数列,比如性别之类重复值很多的字段,基数越小越好。

关于分区表和分区索引(About Partitioned Tables and Indexes)对于10gR2而言,基本上可以分成几类:

   Range(范围)分区

   Hash(哈希)分区

   List(列表)分区

   以及组合分区:Range-Hash,Range-List。

  对于表而言(常规意义上的堆组织表),上述分区形式都可以应用(甚至可以对某个分区指定compress属性),只不过分区依赖列不能是lob,long之类数据类型,每个表的分区或子分区数的总数不能超过1023个。

  对于索引组织表,只能够支持普通分区方式,不支持组合分区,常规表的限制对于索引组织表同样有效,除此之外呢,还有一些其实的限制,比如要求索引组织表的分区依赖列必须是主键才可以等。

  注:本篇所有示例仅针对常规表,即堆组织表!

  对于索引,需要区分创建的是全局索引,或本地索引:

  l 全局索引(global index):即可以分区,也可以不分区。即可以建range分区,也可以建hash分区,即可建于分区表,又可创建于非分区表上,就是说,全局索引是完全独立的,因此它也需要我们更多的维护操作。

  l 本地索引(local index):其分区形式与表的分区完全相同,依赖列相同,存储属性也相同。对于本地索引,其索引分区的维护自动进行,就是说你add/drop/split/truncate表的分区时,本地索引会自动维护其索引分区。

  Oracle建议如果单个表超过2G就最好对其进行分区,对于大表创建分区的好处是显而易见的,这里不多论述why,而将重点放在when以及how。

  WHEN

  一、When使用Range分区

  Range分区呢是应用范围比较广的表分区方式,它是以列的值的范围来做为分区的划分条件,

将记录存放到列值所在的range分区中,比如按照时间划分,2008年1季度的数据放到a分区,08年2季度的数据放到b分区,因此在创建的时候呢,需要你指定基于的列,以及分区的范围值,如果某些记录暂无法预测范围,可以创建maxvalue分区,所有不在指定范围内的记录都会被存储到maxvalue所在分区中,并且支持指定多列做为依赖列,后面在讲how的时候会详细谈到。

  二、When使用Hash分区

  通常呢,对于那些无法有效划分范围的表,可以使用hash分区,这样对于提高性能还是会有一定的帮助。hash分区会将表中的数据平均分配到你指定的几个分区中,列所在分区是依据分区列的hash值自动分配,因此你并不能控制也不知道哪条记录会被放到哪个分区中,hash分区也可以支持多个依赖列。

  三、When使用List分区

  List分区与range分区和hash分区都有类似之处,该分区与range分区类似的是也需要你指定列的值,但这又不同与range分区的范围式列值---其分区值必须明确指定,也不同与hash分区---通过明确指定分区值,你能控制记录存储在哪个分区。它的分区列只能有一个,而不能像range或者hash分区那样同时指定多个列做为分区依赖列,不过呢,它的单个分区对应值可以是多个。

  你在分区时必须确定分区列可能存在的值,一旦插入的列值不在分区范围内,则插入/更新就会失败,因此通常建议使用list分区时,要创建一个default分区存储那些不在指定范围内的记录,类似range分区中的maxvalue分区。

  四、When使用组合分区

  如果某表按照某列分区之后,仍然较大,或者是一些其它的需求,还可以通过分区内再建子分区的方式将分区再分区,即组合分区的方式。

  组合分区呢在10g中有两种:range-hash,range-list。注意顺序哟,根分区只能是range分区,子分区可以是hash分区或list分区。

  提示:11g在组合分区功能这块有所增强,又推出了range-range,list-range,list-list,list-hash,这就相当于除hash外三种分区方式的笛卡尔形式都有了。为什么会没有hash做为根分区的组合分区形式呢,再仔细回味一下第二点,你一定能够想明白~~。

Oracle数据库中,有两种类型的分区索引,全局索引和本地索引,其中本地索引又可以分为本地前缀索引和本地非前缀索引。下面就分别看看每种类型的索引各自的特点。

全局索引以整个表的数据为对象建立索引,索引分区中的索引条目既可能是基于相同的键值但是来自不同的分区,也可能是多个不同键值的组合。

全局索引既允许索引分区的键值和表分区键值相同,也可以不相同。全局索引和表之间没有直接的联系,这一点和本地索引不同。

SQL> create table orders (

order_no      number,

part_no       varchar2(40),

ord_date      date

)

partition by range (ord_date)

(partition Q1 values less than (TO_DATE(‘01-APR-1999‘,‘DD-MON-YYYY‘)),

partition Q2 values less than (TO_DATE(‘01-JUL-1999‘,‘DD-MON-YYYY‘)),

partition Q3 values less than (TO_DATE(‘01-OCT-1999‘,‘DD-MON-YYYY‘)),

partition Q4 values less than (TO_DATE(‘01-JAN-2000‘,‘DD-MON-YYYY‘))

)

;

Table created.

SQL> create index orders_global_1_idx

on orders(ord_date)

global partition by range (ord_date)

(partition GLOBAL1 values less than (TO_DATE(‘01-APR-1999‘,‘DD-MON-YYYY‘)),

partition GLOBAL2 values less than (TO_DATE(‘01-JUL-1999‘,‘DD-MON-YYYY‘)),

partition GLOBAL3 values less than (TO_DATE(‘01-OCT-1999‘,‘DD-MON-YYYY‘)),

partition GLOBAL4 values less than (MAXVALUE)

)

;

Index created.

SQL> create index orders_global_2_idx

on orders(part_no)

global partition by range (part_no)

(partition IND1 values less than (555555),

partition IND2 values less than (MAXVALUE)

)

;

Index created.

从上面的语句可以看出,全局索引和表没有直接的关联,必须显式的指定maxvalue值。假如表中新加了分区,不会在全局索引中自动增加新的分区,必须手工添加相应的分区。

SQL> alter table orders add partition Q5 values less than (TO_DATE(‘01-APR-2000‘,‘DD-MON-YYYY‘));

Table altered.

SQL> select TABLE_NAME, PARTITION_NAME from dba_tab_partitions where table_name=‘ORDERS‘;

TABLE_NAME                     PARTITION_NAME

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

ORDERS                         Q1

ORDERS                         Q2

ORDERS                         Q3

ORDERS                         Q4

ORDERS                         Q5

SQL> select INDEX_NAME, PARTITION_NAME from dba_ind_partitions where index_name=upper(‘orders_global_1_idx‘);

INDEX_NAME                     PARTITION_NAME

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

ORDERS_GLOBAL_1_IDX            GLOBAL1

ORDERS_GLOBAL_1_IDX            GLOBAL2

ORDERS_GLOBAL_1_IDX            GLOBAL3

ORDERS_GLOBAL_1_IDX            GLOBAL4

使用全局索引,索引键值必须和分区键值相同,这就是所谓的前缀索引。Oracle不支持非前缀的全局分区索引,如果需要建立非前缀分区索引,索引必须建成本地索引。

SQL> create index orders_global_2_idx

2  on orders(part_no)

3   global partition by range (order_no)

4    (partition IND1 values less than (555555),

5     partition IND2 values less than (MAXVALUE)

6    )

7  ;

global partition by range (order_no)

*

ERROR at line 3:

ORA-14038: GLOBAL partitioned index must be prefixed

接下来再来看看本地分区。

本地索引的分区和其对应的表分区数量相等,因此每个表分区都对应着相应的索引分区。使用本地索引,不需要指定分区范围因为索引对于表而言是本地的,当本地索引创建时,Oracle会自动为表中的每个分区创建独立的索引分区。

创建本地索引不必显式的指定maxvalue值,因为为表新添加表分区时,会自动添加相应的索引分区。

create index orders_local_1_idx

on orders(ord_date)

local

(partition LOCAL1,

partition LOCAL2,

partition LOCAL3,

partition LOCAL4

)

;

Index created.

SQL> select INDEX_NAME, PARTITION_NAME from dba_ind_partitions where index_name=upper(‘orders_local_1_idx‘);

INDEX_NAME                     PARTITION_NAME

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

ORDERS_LOCAL_1_IDX             LOCAL1

ORDERS_LOCAL_1_IDX             LOCAL2

ORDERS_LOCAL_1_IDX             LOCAL3

ORDERS_LOCAL_1_IDX             LOCAL4

SQL> alter table orders add partition Q5 values less than (TO_DATE(‘01-APR-2000‘,‘DD-MON-YYYY‘));

Table altered.

SQL> select INDEX_NAME, PARTITION_NAME from dba_ind_partitions where index_name=upper(‘orders_local_1_idx‘);

INDEX_NAME                     PARTITION_NAME

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

ORDERS_LOCAL_1_IDX             LOCAL1

ORDERS_LOCAL_1_IDX             LOCAL2

ORDERS_LOCAL_1_IDX             LOCAL3

ORDERS_LOCAL_1_IDX             LOCAL4

ORDERS_LOCAL_1_IDX             Q5

这里系统已经自动以和表分区相同的名字自动创建了一个索引分区。同理,删除表分区时相对应的索引分区也自动被删除。

本地索引和全局索引还有一个显著的差别,就是上面提到的,本地索引可以创建成本地非前缀型,而全局索引只能是前缀型。

SQL> create index orders_local_2_idx

on orders(part_no)

local

(partition LOCAL1,

partition LOCAL2,

partition LOCAL3,

partition LOCAL4)

;

Index created.

SQL> select INDEX_NAME, PARTITION_NAME, HIGH_VALUE from dba_ind_partitions

where index_name=upper(‘orders_local_2_idx‘);

INDEX_NAME                     PARTITION_NAME                 HIGH_VALUE

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

ORDERS_LOCAL_2_IDX             LOCAL1                         TO_DATE(‘ 1999-04-01 00:00:00‘, ‘SYYYY-MM-DD HH24:MI:SS‘,

‘NLS_CALENDAR=GREGORIA‘

ORDERS_LOCAL_2_IDX             LOCAL2                         TO_DATE(‘ 1999-07-01 00:00:00‘, ‘SYYYY-MM-DD HH24:MI:SS‘,

‘NLS_CALENDAR=GREGORIA‘

ORDERS_LOCAL_2_IDX             LOCAL3                         TO_DATE(‘ 1999-10-01 00:00:00‘, ‘SYYYY-MM-DD HH24:MI:SS‘,

‘NLS_CALENDAR=GREGORIA‘

ORDERS_LOCAL_2_IDX             LOCAL4                         TO_DATE(‘ 2000-01-01 00:00:00‘, ‘SYYYY-MM-DD HH24:MI:SS‘,

‘NLS_CALENDAR=GREGORIA‘

从上面的输出可以看出,虽然索引的键值是part_no,但索引分区的键值仍然和表的分区键值相同,即ord_date,也即是所谓的非前缀型索引。

最后,再引用一个例子说明前缀索引和非前缀索引的应用。

假设有一个使用DATE列分区的大表。我们经常使用一个VARCHAR2列(VCOL)进行查询,但这个列并不是表的分区键值。

有两种可能的方法来访问VCOL列的数据,一是建立基于VCOL列的本地非前缀索引,

|                                         |

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

|       |         (10 more                |        |

Values:     A..     Z..   partitions here)            A..      Z..

另一种是建立基于VCOL列的全局索引,

|                                         |

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

|       |         (10 more                |        |

Values:      A..     D..   partitions here)            T..      Z..

可以看出,如果能够保证VCOL列值的唯一性,全局索引将会是最好的选择。如果VCOL列值不唯一,就需要在本地非前缀索引的并行查询和全局索引顺序查询以及高昂的维护代价之间做出选择。

Oracle B-tree索引的浅析

如果聚簇因子过大,那么重建索引可能会有好处,聚簇因子应该接近块的数量,而非行的数量。

Oracle索引经典的神话

·索引会随着时间的增加而变的不平衡;

·删除的索引空间不会被重用;

·随着索引层数的增加,索引将会变得无效并需要重建;

·聚簇因子差,索引需要重建;

·为了提高性能,索引需要经常重建;

索引基础

·一个更新由一个删除和一个插入组成;

·页块由索引条目(row header(2/3B)|length(1B)|indexed data value(nB)|length(1B)|RowID(6B)

)和相应的rowid组成;

·每个页块包含两个指针分别前面的页块和后面页块;

Treedump

alter session set events ‘immediate trace name treedump level index_object_id’;

----- begin tree dump

branch: 0x424362 4342626 (0: nrow: 2, level: 1)

leaf: 0x424363 4342627 (-1: nrow: 540 rrow: 540)

leaf: 0x424364 4342628 (0: nrow: 461 rrow: 461)

----- end tree dump

以上dump包含的信息如下:

块类型:

branch(分支块);leaf(页块);

块地址:0x424362 4342626;

nrow:索引条目的数量;

rrow:当前块中的索引条目数量;

level:分支块等级(页块隐示为0);

Block Dump

alter system dump datafile X block X;

alter system dump datafile X block min X1 block max X2

Start dump data blocks tsn: 0 file#: 1 minblk 148538 maxblk 148538

buffer tsn: 0 rdba: 0x0042443a (1/148538)

scn: 0x0000.00162a95 seq: 0x01 flg: 0x04 tail: 0x2a950601

frmt: 0x02 chkval: 0x8b5c type: 0x06=trans data

Block header dump:  0x0042443a

Object id on Block? Y

seg/obj: 0xd1fe  csc: 0x00.162a95  itc: 2  flg: O  typ: 2 - INDEX

fsl: 0  fnx: 0x42443b ver: 0x01

Itl           Xid                  Uba         Flag  Lck        Scn/Fsc

0x01   0x0005.02a.00000332  0x008005cb.020e.01  CB--    0  scn 0x0000.00162a92

0x02   0x0008.011.00000346  0x008002e6.0163.03  C---    0  scn 0x0000.00162a93

该dump包含的信息如下:

rdba:分支块的相对数据库块地址(文件号/块号);

scn:块最后改变的SCN号;

type:块类型;

seq:块改变的数量;

seg/obj: 16进制对象ID;

typ:段类型;

Itl:相关的事务槽(页块默认为2),包括槽ID,事务ID,撤销块地址,标记,锁信息,和事务SCN;

通过rba确定数据文件号和块号:

select    DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(rba),

DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(rba)

from dual;

通用的索引块头

header address 153168988=0x9212c5c

kdxcolev 0

KDXCOLEV Flags = - - -

kdxcolok 1

kdxcoopc 0x89: opcode=9: iot flags=--- is converted=Y

kdxconco 2

kdxcosdc 2

kdxconro 254

kdxcofbo 544=0x220

kdxcofeo 4482=0x1182

kdxcoavs 3938

kdxcolev:索引级别(0代表页块);

kdxcolok:标示结构块事块是否发生;

kdxcoopc:内部操作码;

kdxconco:索引列数量,包括ROWID;

kdxcosdc:块中索引结构改变的数量;

kdxconro:索引条目的数量,不包括kdxbrlmc指针;

kdxcofbo:块中空闲空间的开始位置;

kdxcofeo:块中空闲空间的结束位置;

kdxcoavs:块中的可用空间数量(kdxcofbo-kdxcofeo);

分支头区域

kdxbrlmc 8388627=0x800013

kdxbrsno 92

kdxbrbksz 8060

kdxbrlmc:如果索引值小于第一个值(row#0),则为该索引值所在的块地址;

kdxbrsno:最后更改的索引条目;

kdxbrbksz:可使用的块空间;

叶块头区域

kdxlespl 0

kdxlende 127

kdxlenxt 4342843=0x42443b

kdxleprv 4342845=0x42443d

kdxledsz 0

kdxlebksz 8036

kdxlespl:块拆分时被清除的未提交数据的字节数;

kdxlende:被删除的条目数;

kdxlenxt:下一个页块的RBA;

kdxleprv:上一个页块的RBA;

kdxlebksz:可使用的块空间(默认小于分支的可用空间);

分支条目

row#0[7898] dba: 4342821=0x424425

col 0; len 3; (3):  c2 61 03

col 1; TERM

row#1[7214] dba: 4342873=0x424459

col 0; len 4; (4):  c3 04 02 17

col 1; TERM

行号,[块中的起始位置] dba;

列号,列长度,列值;

brach中的每个entry有2个columns:

一个是child blocks中的最大值,另一个是指向的下一层block的address‘

但是某些时候可能会有一些比较奇怪的结果:

row#0[7025] dba: 4342908=0x42447c

col 0; len 1024; (1024):

41 20 20 20 …20

col 1; len 4; (4):  00 42 44 73

----- end of branch block dump -----

具体待补充。。。

叶条目

row#38[5014] flag: ----S-, lock: 2, len=14

col 0; len 4; (4):  c3 04 61 55

col 1; len 6; (6):  00 42 43 db 00 a1

row#39[5028] flag: ---DS-, lock: 2, len=14

行号[在块中的开始位置] 各种标记(锁信息,删除信息);

索引列号,长度,值。其中6个字节的为ROWID号,将其转换为二进制,算法结果为:

前10 bit代表了file_id

中22 bit代表了block_id

后16 bit代表了row_id;

通过文件号和块号算出的结果为创建该索引的表的块。

奇怪的是,为什么索引中的rowid不能直接找到obj_id?

因为索引段对应的数据段在 一开始就知道,因为是先知道数据段才找到索引段,然后

根据索引段内容去搜索数据段内容,所以索引段中 rowid 不必包含 data_object_id 信息。

如果索引是建立在非分区表上,或者是分区表上的 LOCAL 索引,使用的是6 bytes的 Restricted ROWID

。如果索引是建立在分区表上的 GLOBAL index,则使用 10bytes 的 Extended ROWID,这样可以区分索引指向哪个分区表。

更新/重用索引条目

当更新了索引条目后,DUMP如下:

kdxconco 2

kdxcosdc 0

kdxconro 2

kdxcofbo 40=0x28

kdxcofeo 8006=0x1f46

kdxcoavs 7966

kdxlespl 0

kdxlende 1

kdxlenxt 0=0x0

kdxleprv 0=0x0

kdxledsz 0

kdxlebksz 8036

row#0[8021] flag: ---D-, lock: 2 => deleted index entry

col 0; len 5; (5): 42 4f 57 49 45

col 1; len 6; (6): 00 80 05 0a 00 00

row#1[8006] flag: -----, lock: 2

col 0; len 5; (5): 5a 49 47 47 59 => new index entry

col 1; len 6; (6): 00 80 05 0a 00 00

更新后,将包含一个删除的条目,一个新的条目。在随后的插入中,如果新插入的索引条目能够放到被删除的索引条目的位置上,就会直接重用这个条目。根据索引值来决定。

所谓重用,是对row 的重用,而不是对row所在物理存储(或说物理位置)的重用。索引是按照indexed value对row进行排序的。有新的row被插入,首先按照value排序,将他放在合适的row list中,如果他的位置正好原来有个row被删掉了,则重用这个row在row list中的位置。至于物理存储上,则可能根据版本不同会有不同。在10.2中,我做的测试并没有向下开辟空间。

结论

·到叶块中的任何插入都将移除所有被删除的条目;

·删除的空间在随后的写中被清除;

·删除的空间在延迟块清除中被清除;

·全空块被放在空闲列表,可以重用;

索引统计

·dba_indexes

·dbms_stats

·index_stats

-- analyze index index_name validate structure;

--分析资源,锁;

·v$segment_statistics

statistics_level = typical (or all)

注意点:

blevel (dba_indexes) vs. height (index_stats)

blocks allocated,但未必使用;

lf_rows_len包含行负载(单列索引12个字节)

pct_used索引结构中当前使用的空间:(used_space/btree_space)*100

大多数索引统计包含删除的条目:

non-deleted rows = lf_rows – del_lf_rows

pct_used by non-deleted rows = ((used_space – del_lf_rows_len) / btree_space) * 100

时间: 2024-10-11 06:19:51

oracle-索引原理的相关文章

oracle索引原理

B-TREE索引 一个B树索引只有一个根节点,它实际就是位于树的最顶端的分支节点.可以用下图一来描述B树索引的结构.其中,B表示分支节点,而L表示叶子节点. 对于分支节点块(包括根节点块)来说,其所包含的索引条目都是按照顺序排列的(缺省是升序排列,也可以在创建索引时指定为降序排列).每 个索引条目(也可以叫做每条记录)都具有两个字段.第一个字段表示当前该分支节点块下面所链接的索引块中所包含的最小键值:第二个字段为四个字节,表示所 链接的索引块的地址,该地址指向下面一个索引块.在一个分支节点块中所

oracle索引总结

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! oracle索引总结 简介 1.说明 1)索引是数据库对象之一,用于加快数据的检索,类似于书籍的索引.在数据库中索引可以减少数据库程序查询结果时需要读取的数据量,类似于在书籍中我们利用索引可以不用翻阅整本书即可找到想要的信息. 2)索引是建立在表上的可选对象:索引的关键在于通过一组排序后的索引键来取代默认的全表扫描检索方式,从而提高检索效率 3)索引在逻辑上和物理上都与相关的表和数据无关,当创建或者删除一个索引时,不会影响基本的表:

数据库索引原理-转

中小企业MIS系统的管理基本上由两大部份组成,一是前台的可视化操作,二是后台的数据库管理.网管对前台的管理和维护工作包括保障网络链路通畅.处理MIS终端的突发事件以及对操作员的管理.培训等,这是网管们日常做得最多.最辛苦的功课:然而MIS系统架构中同等重要的针对数据库的管理.维护和优化工作,现实中似乎并没有得到网管朋友的足够重视,看起来这都是程序员的事,事实上,一个网管如果能在MIS设计期间就数据表的规范化.表索引优化.容量设计.事务处理等诸多方面与程序员进行卓有成效的沟通和协作,那么日常的前台

Oracle 索引&lt;七&gt;

Oracle 索引 管理索引-原理介绍 ? 介绍 索引是用于加速数据存取的数据对象.合理的使用索引可以大大降低 i/o 次数,从而提高数据访问性能.索引有很多种我们主要介绍常用的几种: 为什么添加了索引后,会加快查询速度呢? 创建索引 ? 单列索引 单列索引是基于单个列所建立的索引,比如: create index 索引名 on 表名(列名); ? 复合索引 复合索引是基于两列或是多列的索引.在同一张表上可以有多个索引,但是要求列的组合必须不同,比如: create index emp_idx1

[转载]MySQL索引原理与慢查询优化

好文,以防丢失,故转之,另对排版做简单优化.原文地址:http://ourmysql.com/archives/1401 索引目的 索引的目的在于提高查询效率,可以类比字典,如果要查"mysql"这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql.如果没有索引,那么你可能需要把所有单词看一遍才能找到你想要的,如果我想找到m开头的单词呢?或者w开头的单词呢?是不是觉得如果没有索引,这个事情根本无法完成? 索引原理 除了词典,生活中随处可见索引的例子,如火车站的

mysql之索引原理与慢查询优化

一.介绍 1.什么是索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句的优化显然是重中之重.说起加速查询,就不得不提到索引了. 2.为什么要有索引呢? 索引在MySQL中也叫做"键",是存储引擎用于快速找到记录的一种数据结构.索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要.索引优化应该是对查询性能优化最有效的手段了.

深入浅出数据库索引原理

前段时间,公司一个新上线的网站出现页面响应速度缓慢的问题, 一位负责这个项目的但并不是搞技术的妹子找到我,让我想办法提升网站的访问速度 ,因为已经有很多用户来投诉了.我第一反应觉的是数据库上的问题,假装思索了一下,摆着一副深沉炫酷的模样说:"是不是数据库查询上出问题了, 给表加上索引吧",然后妹子来了一句:"现在我们网站访问量太大,加索引有可能导致写入数据时性能下降,影响用户使用的".当时我就楞了一下, 有种强行装逼被拆穿的感觉,在自己的专业领域居然被非专业的同学教

MySQL索引原理及慢查询优化

原文:http://tech.meituan.com/mysql-index.html MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”.“SQL语句优化”.“了解数据库原理”等要求.我们知道一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一

MySQL索引原理及慢查询优化(转)

原文:http://tech.meituan.com/mysql-index.html MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”.“SQL语句优化”.“了解数据库原理”等要求.我们知道一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一

Oracle 索引 简介

1 索引的创建语法: CREATE UNIUQE | BITMAP INDEX <schema>.<index_name> ON <schema>.<table_name> (<column_name> | <expression> ASC | DESC, <column_name> | <expression> ASC | DESC,...) TABLESPACE <tablespace_name>