唯一性索引(Unique Index)与普通索引(Normal Index)差异(中)

时间:2021-08-23 08:17:30

声明:本篇知识方法受到dbsnake相关文章启发,特此感谢!

 

在本系列的前篇(http://space.itpub.net/17203031/viewspace-700089)里,我们探讨了唯一索引和普通索引在应用角度上的差异。实验中,我们发现在基础数据相同的情况下,两类型索引在体积上有细微的差异,这使得我们可以猜测两种类型索引在存储结构上的可能差异。

 

本篇打算从存储结构入手,探讨两种类型索引的差异。

 

1、  环境准备

 

同前篇相同,准备相同的数据索引列取值,创建不同类型的索引。为了减少整理数据量,所以选择较小的数据集。

 

 

SQL> select * from v$version where rownum<2;

BANNER

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

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

 

SQL> create table t (id number, vid number);

Table created

 

SQL> select * from t;

        ID        VID

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

         1          1

         2          2

         3          3

 

//创建普通索引

SQL> create index idx_t_normalid on t(id);

Index created

 

//创建唯一性索引

SQL> create unique index idx_t_uniqueid on t(vid);

Index created

 

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

SQL> col object_name for a20;

SQL> select object_name, object_id from dba_objects where object_name in ('IDX_T_NORMALID','IDX_T_UNIQUEID');

 

OBJECT_NAME           OBJECT_ID

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

IDX_T_NORMALID            75141

IDX_T_UNIQUEID            75142

 

 

 

2、  普通索引逻辑结构分析

 

思路是从索引树入手,现将索引段结构Dump出去,检查整体分布情况。

 

//获取跟踪文件位置

SQL> select name, value from  v$diag_info where name='Default Trace File';

 

NAME                           VALUE

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

Default Trace File             /u01/diag/rdbms/wilson/wilson/trace/wilson_ora_5850.trc

 

//将索引树Dump出到Trace文件

SQL> alter session set events 'immediate trace name treedump level 75141';

Session altered

 

 

注意:v$diag_info视图是Oracle 11g中新推出的视图类型,其中包括了所有诊断文件的位置和目录。其中Default Trace File项目就是当前会话对应的Trace File路径和名称,不需要我们过去那样使用自定义函数查询。

 

此时,我们检索Trace File内容,核心片段如下:

 

 

*** 2011-06-15 01:47:04.350

*** SESSION ID:(20.13) 2011-06-15 01:47:04.350

*** CLIENT ID:() 2011-06-15 01:47:04.350

*** SERVICE NAME:(wilson) 2011-06-15 01:47:04.350

*** MODULE NAME:(PL/SQL Developer) 2011-06-15 01:47:04.350

*** ACTION NAME:(Command Window - New) 2011-06-15 01:47:04.350

 

----- begin tree dump

leaf: 0x415af1 4283121 (0: nrow: 3 rrow: 3)

----- end tree dump

 

上面的Tree Dump结果,说明因为索引很小(只有三行记录),只有一个索引块中包括数据。其中叶子节点为3个,该索引块的地址为(0x415af1,十进制表示为4283121)。

 

索引块4283121对应的实际文件和数据块信息是什么呢?使用dbms_utiliy包的相关方法,可以帮助获取到对应文件和块号。

 

 

SQL> select dbms_utility.data_block_address_file(4283121), dbms_utility. data_block_address_block(4283121) from dual;

 

DBMS_UTILITY.DATA_BLOCK_ADDRES DBMS_UTILITY.DATA_BLOCK_ADDRES

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

                             1                          88817

 

 

索引块在文件1,对应块号为88817

 

使用逻辑dump出块的结构信息。

 

 

SQL> alter system dump datafile 1 block 88817;

System altered

 

 

DUMP在跟踪文件上的核心内容为:

 

 

--核心片段

Block dump from cache:

Dump of buffer cache at level 4 for tsn=0, rdba=4283121

BH (0x2dff3578) file#: 1 rdba: 0x00415af1 (1/88817) class: 1 ba: 0x2dea0000

 

Block header dump:  0x00415af1

 Object id on Block? Y

 seg/obj: 0x12585  csc: 0x00.396f48  itc: 2  flg: -  typ: 2 - INDEX

     fsl: 0  fnx: 0x0 ver: 0x01

 

row#0[8020] flag: ------, lock: 0, len=12

col 0; len 2; (2):  c1 02

col 1; len 6; (6):  00 41 5a e9 00 00

row#1[8008] flag: ------, lock: 0, len=12

col 0; len 2; (2):  c1 03

col 1; len 6; (6):  00 41 5a e9 00 01

row#2[7996] flag: ------, lock: 0, len=12

col 0; len 2; (2):  c1 04

col 1; len 6; (6):  00 41 5a e9 00 02

----- end of leaf block dump -----

End dump data blocks tsn: 0 file#: 1 minblk 88817 maxblk 88817

 

 

实际Dump出的信息很多,篇幅原因,笔者只是将最核心的部分加以展现。

 

首先关注最后的三个row#信息,该部分表示三个叶子节点的核心内容。其中[]中包括的是叶子节点在索引块中的相对偏移量。Len表示该叶子所占用空间长度是多少(注意:此处的Normal Index叶子节点占用12长度)。

 

最后,观察行与行之间的偏移量差(7996-8008-8020),正好是行间的距离12

 

每个叶子节点包括两个column内容,分别占据2位和6位。由于是使用16进制形式保存,我们不能直接看出内容含义。下面实验可以帮助我们进行猜测:

 

 

--关键数据演算:

 

SQL> select dump(1,1016) from dual;

DUMP(1,1016)

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

Typ=2 Len=2: c1,2

 

SQL> select dump(2,1016) from dual;

DUMP(2,1016)

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

Typ=2 Len=2: c1,3

 

SQL> select dump(3,1016) from dual;

DUMP(3,1016)

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

Typ=2 Len=2: c1,4

 

 

我们的索引列取值就是1234,上面使用DUMP函数的结果,告诉我们跟踪文件中每个row的第一列表示的是索引列取值。索引叶子节点内容无非就是rowid和索引键值。那么row的第二列含义必然可能就是rowid相关内容。

 

只对第三行数据进行试算:

 

 

16进制取值:00 41 5a e9 00 02

 2进制取值:00000000 01000001 01011010 11101001 00000000 00000010

 

 

根据相关资料,我们进行如下设定:

 

 

File_ID:前10位:00000000 01=1

Block_id 中间22位:000001 01011010 11101001=88809

Slot_id00000000 00000010=2

 

 

如果改值是rowid,那么一定和数据表T行的rowid存在对应关系。

 

 

SQL> select t.*,t.rowid, dbms_rowid.rowid_relative_fno(t.rowid) fno, dbms_rowid.rowid_block_number(t.rowid)

blockn,dbms_rowid.rowid_row_number(t.rowid) rown from t;

 

        ID        VID ROWID                     FNO     BLOCKN       ROWN

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

         1          1 AAASWCAABAAAVrpAAA          1      88809          0

         2          2 AAASWCAABAAAVrpAAB          1      88809          1

         3          3 AAASWCAABAAAVrpAAC          1      88809          2

 

 

Rowid中包括的信息:文件编号+对象编号+块编号+块内slot编号;索引叶子中的第二列数据,已经能够提供文件编号、块编号和内部slot编号。对象编号是进行选取Select时候就带入的。

 

 

3、普通索引物理结构分析

 

从上面的逻辑结构,我们看到了存储的逻辑结构。下面根据信息,我们到物理存储层面看普通索引结构。注意,此处我们只关注一行数据,就是row2[7996]的叶子节点。

 

我们现在有该索引的文件编号和块编号,缺乏就是实际的偏移量。下面首先计算出实际的偏移量。

 

 

SQL> select file_name, tablespace_name from dba_data_files where file_id=1;

 

FILE_NAME                                                                        TABLESPACE_NAME

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

/u01/oradata/WILSON/datafile/o1_mf_system_6bcsnqfc_.dbf                          SYSTEM

 

 

SQL> select tablespace_name,SEGMENT_SPACE_MANAGEMENT from dba_tablespaces where tablespace_name='SYSTEM';

 

TABLESPACE_NAME                SEGMENT_SPACE_MANAGEMENT

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

SYSTEM                         MANUAL

 

 

数据表索引对应的表空间SYSTEM,是使用MSSM进行Segment Space Management。所以,计算出的偏移量就是:

 

 

7996+68+(2-1)*24=8088

 

 

下面使用BBED工具直接进行块读取,注意,首先需要关闭数据库。

 

 

==dump

[oracle@oracle11g test]$ bbed parfile=par.txt

Password:

 

BBED: Release 2.0.0.0.0 - Limited Production on Wed Jun 15 02:05:20 2011

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

************* !!! For Oracle Internal Use only !!! ***************

 

BBED> info  

 File#  Name                                                        Size(blks)

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

     1  /u01/oradata/WILSON/datafile/o1_mf_system_6bcsnqfc_.dbf         106240

     (篇幅原因,有省略……

 

//定位到指定的文件和块位置(也可以使用set file 1 block 88817

BBED> set dba 0x00415af1

        DBA             0x00415af1 (4283121 1,88817)

 

//定位块内的偏移量

BBED> set offset 8088

        OFFSET          8088

 

//DUMP显示出来

BBED> dump

 File: /u01/oradata/WILSON/datafile/o1_mf_system_6bcsnqfc_.dbf (1)

 Block: 88817            Offsets: 8088 to 8191           Dba:0x00415af1

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

 000002c1 04060041 5ae90002 000002c1 03060041 5ae90001 000002c1 02060041

 5ae90000 0d000040 15000002 00401500 000203c2 085eac00 01150015 00004015

 00000100 40150000 0103c208 5aac0001 11001100 0040110f 00090040 110f0009

 03c20858 0106496f

 

 <32 bytes per line>

 

 

再看一下我们将row2导出的信息:

 

 

row#2[7996] flag: ------, lock: 0, len=12

col 0; len 2; (2):  c1 04

col 1; len 6; (6):  00 41 5a e9 00 02

 

 

注意:两个标记0x020x06的作用是标记列Column信息,用于分割。

 

4、  局部结论

 

对普通索引结构来说,索引叶子节点上保存索引键值和对应数据行rowid两者是以行中不同column的方式进行存储,键值在前,rowid在后。

 

 

下面我们使用相同的流程处理Unique Index,检查相关的存储结构。