声明:本篇知识方法受到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
我们的索引列取值就是1、2、3、4,上面使用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_id:00000000 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
注意:两个标记0x02和0x06的作用是标记列Column信息,用于分割。
4、 局部结论
对普通索引结构来说,索引叶子节点上保存索引键值和对应数据行rowid。两者是以行中不同column的方式进行存储,键值在前,rowid在后。
下面我们使用相同的流程处理Unique Index,检查相关的存储结构。