Oracle 的 data block研读(二)

    上次,dump了DATA的block结构信息出来,简单看了一下,今天,把INDEX的block也dump出来,看看里面的内容。
    B 树索引是一个典型的树结构,其包含的组件主要是:

<!--[if !supportLists]--&gt1)     <!--[endif]--&gt叶子节点(Leaf node):包含条目直接指向表里的数据行。

<!--[if !supportLists]--&gt2)     <!--[endif]--&gt分支节点(Branch node):包含的条目指向索引里其他的分支节点或者是叶子节点。

<!--[if !supportLists]--&gt3)     <!--[endif]--&gt根节点(Root node):一个B树索引只有一个根节点,它实际就是位于树的最顶端的分支节点。

    首先,确定要dump的索引名称,在已有系统中,找到一个平时用的比较多的表,看看上面的索引INDEX_ORAGNISE。
SQL> select data_object_id,object_id from dba_objects where object_name='INDEX_ORAGNISE';
DATA_OBJECT_ID  OBJECT_ID
-------------- ----------
         88217      88217

找到索引对应的OBJECT_ID,我们再用treedump来dump对应的OBJECT。
SQL> alter session set events 'immediate trace name treedump level 88217';
Session altered
SQL>
 
dump完成,我们再定位到user_dump_dest定义的目录下,找得刚才dump出来的trace文件。
SQL> show parameter user_dump_dest
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
user_dump_dest                       string      /oracle/admin/orcl/udump
 
打开trace文件,里面内容如下:
*** SESSION ID:(125.177) 2010-12-27 11:18:57.516
----- begin tree dump
branch: 0x140110c 20975884 (0: nrow: 51, level: 1)
   leaf: 0x140110d 20975885 (-1: nrow: 372 rrow: 372)
   leaf: 0x140110e 20975886 (0: nrow: 336 rrow: 336)
   leaf: 0x140110f 20975887 (1: nrow: 340 rrow: 340)
   leaf: 0x1401110 20975888 (2: nrow: 336 rrow: 336)
   leaf: 0x1401111 20975889 (3: nrow: 338 rrow: 338)
   leaf: 0x1401112 20975890 (4: nrow: 338 rrow: 338)
    。
    。
    。
   leaf: 0x1401141 20975937 (48: nrow: 438 rrow: 438)
   leaf: 0x1401142 20975938 (49: nrow: 29 rrow: 29)
----- end tree dump
 
表示共有52个索引块,其中 branch(表示为根节点)1个,leaf(表示为叶子节点)51个。
####################################################################
结构说明:

leaf:表示该数据块是leaf block
0x140110d:对应索引数据块的十六进位地址
20975885:对应索引数据块的十进位地址
-1: 表示索引数据块的编号,编号起始是-1
nrow: 372 :表示该索引数据块中总的行数,包含被删除的行
rrow: 372:表示该索引数据块中实际存在有效行数
####################################################################

通过视图查询索引的详细信息如下:
 
SQL> analyze index INDEX_ORAGNISE validate structure;
Index analyzed
SQL> SELECT NAME, BLOCKS, HEIGHT,LF_ROWS, LF_BLKS, BR_ROWS, BR_BLKS, BTREE_SPACE, USED_SPACE FROM INDEX_STATS WHERE NAME='INDEX_ORAGNISE';
NAME                               BLOCKS     HEIGHT    LF_ROWS    LF_BLKS    BR_ROWS    BR_BLKS BTREE_SPACE USED_SPACE
------------------------------ ---------- ---------- ---------- ---------- ---------- ---------- ----------- ----------
INDEX_ORAGNISE                     72          2      17290         51         50          1      415824     360985
SQL>
 
根据上面的trace信息,得到文件号和块号。如果索引很小,那么就可能没有branch,直接从leaf取。如果仅仅看某个leaf的内容,也可以直接从leaf的dba得到文件号和块号。
 
根结点文件号:
SQL> select dbms_utility.data_block_address_file(20975884) "file",
  2         dbms_utility.data_block_address_block(20975884) "block"
  3    from dual;
      file      block
---------- ----------
         5       4364
 
叶节点文件号:
SQL> select dbms_utility.data_block_address_file(20975886) "file",
  2         dbms_utility.data_block_address_block(20975886) "block"
  3    from dual;
      file      block
---------- ----------
         5       4366
 
再dump该block得到实际INDEX block的内容。
SQL> alter system dump datafile 5 block 4364;
System altered
SQL>
 
重新打开刚才的trace文件,可以看到如下内容:
Branch block dump  (开始根节点块信息)
=================
header address 381738060=0x16c0dc4c
kdxcolev 1
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: pcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 50
kdxcofbo 128=0x80
kdxcofeo 7364=0x1cc4
kdxcoavs 7236
kdxbrlmc 20975885=0x140110d
kdxbrsno 0
kdxbrbksz 8056
kdxbr2urrc 3
row#0[8040] dba: 20975886=0x140110e
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 34
col 1; TERM
row#1[8027] dba: 20975887= 0x140110f
col 0; len 7; (7):  31 33 30 34 33 32 30
col 1; TERM
row#2[8014] dba: 20975888=0x1401110
col 0; len 7; (7):  31 33 30 39 30 30 31
col 1; TERM
row#3[8004] dba: 20975889=0x1401111
col 0; len 4; (4):  31 34 30 33
col 1; TERM
row#4[7988] dba: 20975890=0x1401112
col 0; len 10; (10):  31 34 32 34 30 30 30 32 30 32
col 1; TERM

     对于分支节点块(包括根节点块)来说,其所包含的索引条目都是按照顺序排列的(缺省是升序排列,也可以在创建索引时指定为降序排列)。每个索引条目(也可以叫做每条记录)都具有两个字段。第一个字段表示当前该分支节点块下面所链接的索引块中所包含的最小键值;第二个字段为四个字节,表示所链接的索引块的地址,该地址指向下面一个索引块。在一个分支节点块中所能容纳的记录行数由数据块大小以及索引键值的长度决定。比如从上可以看到,对于根节点块来说,包含五条记录,它们指向五个分支节点块。其中col 0对应的数值分别表示分支节点块所链接的键值的最小值。而如dba:dba: 20975887=0x140110f则表示所指向的五个分支节点块的地址,包括十进制与十六进制地址。而col 1没看出有什么用,还希望有高手指点。

根据以上内容中,branch 20975886 得到更详细内容

SQL> select dbms_utility.data_block_address_file(20975886) "file",
  2         dbms_utility.data_block_address_block(20975886) "block"
  3    from dual;
      file      block
---------- ----------
         5       4366
 
Leaf block dump
===============
header address 381738084=0x16c0dc64
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: pcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 336
kdxcofbo 708=0x2c4
kdxcofeo 1530=0x5fa
kdxcoavs 822
kdxlespl 0
kdxlende 0
kdxlenxt 20975887= 0x140110f -- 下个节点RBA地址
kdxleprv 20975885=0x140110d -- 上一个节点RBA地址
kdxledsz 0
kdxlebksz 8032
row#0[8012] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 34
col 1; len 6; (6):  01 40 10 70 00 48
row#1[7992] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 35
col 1; len 6; (6):  01 40 10 4d 00 19
row#2[7972] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 36
col 1; len 6; (6):  01 40 11 08 00 0f
row#3[7952] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 37
col 1; len 6; (6):  01 40 10 4d 00 26
row#4[7932] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 38
col 1; len 6; (6):  01 40 10 6c 00 39
row#5[7912] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 39
col 1; len 6; (6):  01 40 10 4d 00 27
row#6[7892] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 32 30
col 1; len 6; (6):  01 40 10 80 00 16
###########################################################################
结构说明:

header address 381738084=0x16c0dc64
kdxcolev 0          (index level, 0表示是leaf block)
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: pcode=0: iot flags=--- is converted=Y 内部操作代码
kdxconco 2 索引列数量
kdxcosdc 0 索引结构改变次数
kdxconro 336 索引记录数量BR_ROWS
kdxcofbo 708=0x2c4 空闲空间开始偏移量
kdxcofeo 1530=0x5fa  空闲空间结束偏移量
kdxcoavs 822  所提供的空闲空间
kdxlespl 0   在数据块被cleaned out时,还未进行commit的数量
kdxlende 0 被删除索引记录的数量
kdxlenxt 20975887=0x140110f 下一个链接数据块的地址
kdxleprv 20975885=0x140110d 上一个链接数据块地址
kdxledsz 0  被删除的空间大小
kdxlebksz 8032 使用的数据块空间

kdxcolev: index level (0 represents leaf blocks)
kdxcolok: denotes whether structural block transaction is occurring
kdxcoopc: internal operation code
kdxconco: index column count
kdxcosdc: count of index structural changes involving block
kdxconro: number of index entries (does not include kdxbrlmc pointer)
kdxcofbo: offset to beginning of free space within block
kdxcofeo: offset to the end of free space (ie. first portion of block containing index data)
kdxcoavs: available space in block (effectively area between the two fields above)
 
 
结构说明:
row#0[8012] flag: ------, lock: 0, len=20
col 0; len 10; (10):  31 33 30 31 30 34 30 30 31 34
col 1; len 6; (6):  01 40 10 70 00 48
col 0: 为keyvalue.
col 1: 为Rowid

关于rowid的换算:
索引中的rowid由48个bit构成,前10个bit构成文件号,中间22个bit构成块号,最后16个bit 构成块中的行号。
(关于rowid解读,参考:我的ROWID解读)

关于整数key值的换算:
先去掉c2,因为c2只表示最高位的位置,后面才是真正数据,06转换为十进制为6,减去1为5,3b转换为十进制为48+11=59,

减去1为 58,两个结果合并到一起,为258,这就是其存储的真正数据,简单表示一下:

   1. c2 03 3b -> 03 3b
   2. 03 -> 0*16+3=3->3-1=2
   3. 3b -> 3*16+14=59 -> 59-1=58
   4. 结果合并得到258

关于字符串的换算:直接由十六进制得到各个ascii码,然后对应到相应字符。

这个换算没有得到验证???????
这种dump信息要参考DSI

注意:上面红色的字体标识该索引块的键值中最小的键值,也就是下面第一个索引叶节点块的第一个索引项对应的数值。
###########################################################################
 
     对于叶子节点块来说,其所包含的索引条目与分支节点一样,都是按照顺序排列的(缺省是升序排列,也可以在创建索引时指定为降序排列)。每个索引条目(也可以叫做每条记录)也具有两个字段。第一个字段表示索引的键值,对于单列索引来说是一个值;而对于多列索引来说则是多个值组合在一起的。第二个字段表示键值所对应的记录行的 ROWID ,该 ROWID 是记录行在表里的物理地址。如果索引是创建在非分区表上或者索引是分区表上的本地索引的话,则该 ROWID 占用 6 个字节;如果索引是创建在分区表上的全局索引的话,则该 ROWID 占用 10 个字节。
 
     知道这些信息以后,我们可以举个例子来说明如何估算每个索引能够包含多少条目,以及对于表来说,所产生的索引大约多大。对于每个索引块来说,缺省的PCTFREE10%,也就是说最多只能使用其中的90%。同时9i以后,这90%中也不可能用尽,只能使用其中的87%左右。也就是说,8KB的数据块中能够实际用来存放索引数据的空间大约为64888192×90%×88%)个字节。

     假设我们有一个非分区表,表名为warecountd,其数据行数为130万行。该表中有一个列,列名为goodid,其类型为char8),那么也就是说该goodid的长度为固定值:8。同时在该列上创建了一个B树索引。

在叶子节点中,每个索引条目都会在数据块中占一行空间。每一行用23个字节作为行头,行头用来存放标记以及锁定类型等信息。同时,在第一个表示索引的键值的字段中,每一个索引列都有1个字节表示数据长度,后面则是该列具体的值。那么对于本例来说,在叶子节点中的一行所包含的数据大致如下图二所示:

bb

从上图可以看到,在本例的叶子节点中,一个索引条目占18个字节。同时我们知道8KB的数据块中真正可以用来存放索引条目的空间为6488字节,那么在本例中,一个数据块中大约可以放3606488/18)个索引条目。而对于我们表中的130万条记录来说,则需要大约36111300000/360)个叶子节点块。

      而对于分支节点里的一个条目(一行)来说,由于它只需保存所链接的其他索引块的地址即可,而不需要保存具体的数据行在哪里,因此它所占用的空间要比叶子节点要少。分支节点的一行中所存放的所链接的最小键值所需空间与上面所描述的叶子节点相同;而存放的索引块的地址只需要4个字节,比叶子节点中所存放的ROWID少了2个字节,少的这2个字节也就是ROWID中用来描述在数据块中的行号所需的空间。因此,本例中在分支节点中的一行所包含的数据大致如下图三所示:

bb

从上图可以看到,在本例的分支节点中,一个索引条目占16个字节。根据上面叶子节点相同的方式,我们可以知道一个分支索引块可以存放大约4056488/16)个索引条目。而对于我们所需要的3611个叶子节点来说,则总共需要大约9个分支索引块。

      这样,我们就知道了我们的这个索引有2层,第一层为1个根节点,第二层为9个分支节点,而叶子节点数为3611个,所指向的表的行数为1300000行。但是要注意,在oracle的索引中,层级号是倒过来的,也就是说假设某个索引有N层,则根节点的层级号为N,而根节点下一层的分支节点的层级号为N-1,依此类推。对本例来说,9个分支节点所在的层级号为1,而根节点所在的层级号为2


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9399028/viewspace-682646/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/9399028/viewspace-682646/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值