内部结构
将B树索引转储成树状结构的形式而呈现出来:
alter session set events ‘immediate trace name treedump level INDEX_OBJECT_ID‘;
SQL> alter session set events ‘immediate trace name treedump level 126545‘;
Session altered.
[[email protected] trace]$ pwd
/home/oracle/app/oracle/diag/rdbms/tonytest/tonytest/trace
[[email protected] trace]$ vi tonytest_ora_5439.trc
----- begin tree dump
branch: 0x34000e3 54526179 (0: nrow: 444, level: 1)
leaf: 0x34000e4 54526180 (-1: nrow: 393 rrow: 393)
leaf: 0x34000e5 54526181 (0: nrow: 377 rrow: 377)
leaf: 0x34000e6 54526182 (1: nrow: 378 rrow: 378)
leaf: 0x34000e7 54526183 (2: nrow: 377 rrow: 377)
leaf: 0x34000e8 54526184 (3: nrow: 377 rrow: 377)
leaf: 0x34000e9 54526185 (4: nrow: 378 rrow: 378)
leaf: 0x34000ea 54526186 (5: nrow: 379 rrow: 379)
leaf: 0x34000eb 54526187 (6: nrow: 379 rrow: 379)
leaf: 0x34000ec 54526188 (7: nrow: 379 rrow: 379)
…………………
----- end tree dump
- 每一行的第一列表示节点类型>:branch表示分支节点(包括根节点),而leaf则表示叶子节点;
- 第二列表示十六进制表示的节点的地址;
- 第三列表示十进制表示的节点的地址;
- 第四列表示相对于前一个节点的位置,根节点从0开始计算,其他分支节点和叶子节点从-1开始计算;
- 第五列的nrow表示当前节点中所含有的索引条目的数量。比如我们可以看到根节点中含有的nrow为444,表示根节点中含有444个索引条目,分别指向444个分支节点;
- 第六列中的rrow表示有效的索引条目(因为索引条目如果被删除,不会立即被清除出索引块中。所以nrow减rrow的数量就表示已经被删除的索引条目数量)的数量,比如对于第一个leaf来说,其rrow为393,也就是说该叶子节点中存放了393个可用索引条目,分别指向表emp3的393条记录。
转储一个索引节点
alter system dump datafile file# block block#;
select dbms_utility.data_block_address_file(54526179),
dbms_utility.data_block_address_block(54526179) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(54526179) DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(54526179)
---------------------------------------------- -----------------------------------------------
13 227
SQL> alter system dump datafile 13 block 227;
System altered.
Branch block dump
=================
header address 140301252250188=0x7f9a6e4d4a4c
kdxcolev 1
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 443
kdxcofbo 914=0x392
kdxcofeo 2739=0xab3
kdxcoavs 1825
kdxbrlmc 54526180=0x34000e4
kdxbrsno 49
kdxbrbksz 8056
kdxbr2urrc 3
- kdxcolev表示索引层级号,这里由于我们转储的是根节点,所以其层级号为1。对叶子节点来说该值为0;
- kdxcolok表示该索引上是否正在发生修改块结构的事务;
- kdxcoopc表示内部操作代码;
- kdxconco表示索引条目中列的数量;
- kdxcosdc表示索引结构发生变化的数量,当你修改表里的某个索引键值时,该值增加;
- kdxconro表示当前索引节点中索引条目的数量,但是注意,不包括kdxbrlmc指针;
- kdxcofbo表示当前索引节点中可用空间的起始点相对当前块的位移量;
- kdxcofeo表示当前索引节点中可用空间的最尾端的相对当前块的位移量;
- kdxcoavs表示当前索引块中的可用空间总量,也就是用kdxcofeo减去kdxcofbo得到的。
- kdxbrlmc表示分支节点的地址,
- kdxbrsno表示最后一个被修改的索引条目号,如果=0,表示该索引是新建的索引;
- kdxbrbksz表示可用数据块的空间大小。实际从这里已经可以看到,即便是PCTFREE设置为0,也不能用足8192字节。
row#0[8043] dba: 54526181=0x34000e5
col 0; len 7; (7): 64 65 76 32 32 34 39
col 1; TERM
row#1[8030] dba: 54526182=0x34000e6
col 0; len 7; (7): 64 65 76 32 36 32 36
col 1; TERM
row#2[8017] dba: 54526183=0x34000e7
col 0; len 7; (7): 64 65 76 33 30 30 33
col 1; TERM
row#3[8005] dba: 54526184=0x34000e8
col 0; len 6; (6): 64 65 76 33 33 38
col 1; TERM
每个索引条目都指向一个分支节点。
col 1表示所链接的分支节点的地址,该值经过一定的转换以后实际就是row#所在行的dba的值。如果根节点下没有其他的分支节点,则col 1为TERM;
col 0表示该分支节点所链接的最小键值。
#索引高度为1
----- begin tree dump
*** 2015-07-10 07:05:03.971
branch: 0x34000e3 54526179 (0: nrow: 479, level: 1)
leaf: 0x34000e4 54526180 (-1: nrow: 272 rrow: 272)
leaf: 0x34004c9 54527177 (0: nrow: 272 rrow: 272)
leaf: 0x34004d3 54527187 (1: nrow: 272 rrow: 272)
leaf: 0x34004ab 54527147 (2: nrow: 272 rrow: 272)
leaf: 0x34004af 54527151 (3: nrow: 272 rrow: 272)
.........
leaf: 0x34004a9 54527145 (473: nrow: 420 rrow: 420)
leaf: 0x34004ad 54527149 (474: nrow: 420 rrow: 420)
leaf: 0x34004b1 54527153 (475: nrow: 420 rrow: 420)
leaf: 0x34004b5 54527157 (476: nrow: 420 rrow: 420)
leaf: 0x34004e2 54527202 (477: nrow: 96 rrow: 96)
----- end tree dump
#模拟插入数据,然后根节点分裂,索引高度为2
SQL> alter session set events ‘immediate trace name treedump level 126545‘;
Session altered.
----- begin tree dump
branch: 0x34000e3 54526179 (0: nrow: 3, level: 2)
branch: 0x34005d7 54527447 (-1: nrow: 253, level: 1)
leaf: 0x34000e4 54526180 (-1: nrow: 272 rrow: 272)
leaf: 0x34005e1 54527457 (0: nrow: 272 rrow: 272)
leaf: 0x34005f1 54527473 (1: nrow: 272 rrow: 272)
leaf: 0x34005fa 54527482 (2: nrow: 272 rrow: 272)
leaf: 0x34005fe 54527486 (3: nrow: 272 rrow: 272)
.........
leaf: 0x3400779 54527865 (246: nrow: 272 rrow: 272)
leaf: 0x3400766 54527846 (247: nrow: 272 rrow: 272)
leaf: 0x340076a 54527850 (248: nrow: 310 rrow: 310)
leaf: 0x340076d 54527853 (249: nrow: 379 rrow: 379)
leaf: 0x34005b9 54527417 (250: nrow: 271 rrow: 271)
leaf: 0x34005bd 54527421 (251: nrow: 272 rrow: 272)
branch: 0x34007e2 54527970 (0: nrow: 256, level: 1)
leaf: 0x34005a7 54527399 (-1: nrow: 272 rrow: 272)
leaf: 0x34005a8 54527400 (0: nrow: 265 rrow: 265)
leaf: 0x34005ac 54527404 (1: nrow: 272 rrow: 272)
leaf: 0x3400597 54527383 (2: nrow: 471 rrow: 471)
leaf: 0x3400582 54527362 (3: nrow: 271 rrow: 271)
leaf: 0x3400586 54527366 (4: nrow: 267 rrow: 267)
.........
leaf: 0x3400273 54526579 (249: nrow: 212 rrow: 212)
leaf: 0x340022c 54526508 (250: nrow: 212 rrow: 212)
leaf: 0x3400230 54526512 (251: nrow: 212 rrow: 212)
leaf: 0x3400277 54526583 (252: nrow: 212 rrow: 212)
leaf: 0x340027b 54526587 (253: nrow: 213 rrow: 213)
leaf: 0x340027f 54526591 (254: nrow: 393 rrow: 393)
branch: 0x34005da 54527450 (1: nrow: 278, level: 1)
leaf: 0x34000ff 54526207 (-1: nrow: 213 rrow: 213)
leaf: 0x3400246 54526534 (0: nrow: 212 rrow: 212)
leaf: 0x3400240 54526528 (1: nrow: 212 rrow: 212)
leaf: 0x3400244 54526532 (2: nrow: 212 rrow: 212)
leaf: 0x3400266 54526566 (3: nrow: 212 rrow: 212)
leaf: 0x340026a 54526570 (4: nrow: 212 rrow: 212)
.........
leaf: 0x34004a5 54527141 (271: nrow: 420 rrow: 420)
leaf: 0x34004a9 54527145 (272: nrow: 420 rrow: 420)
leaf: 0x34004ad 54527149 (273: nrow: 420 rrow: 420)
leaf: 0x34004b1 54527153 (274: nrow: 420 rrow: 420)
leaf: 0x34004b5 54527157 (275: nrow: 420 rrow: 420)
leaf: 0x34004e2 54527202 (276: nrow: 96 rrow: 96)
----- end tree dump
Branch block dump
=================
header address 140408996839500=0x7fb38461404c
kdxcolev 2
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 1
kdxconro 2
kdxcofbo 32=0x20
kdxcofeo 8030=0x1f5e
kdxcoavs 7998
kdxbrlmc 54527447=0x34005d7
kdxbrsno 0
kdxbrbksz 8056
kdxbr2urrc 3
row#0[8030] dba: 54527970=0x34007e2
col 0; len 3; (3): 64 65 76
col 1; len 6; (6): 03 40 06 dc 00 92
row#1[8045] dba: 54527450=0x34005da
col 0; len 5; (5): 71 61 33 33 39
col 1; TERM
----- end of branch block dump -----
End dump data blocks tsn: 19 file#: 13 minblk 227 maxblk 227
索引访问
当oracle需要访问索引里的某个索引条目时,oracle是如何找到该索引条目所在的数据块的呢?
当oracle进程需要访问数据文件里的数据块时,oracle会有两种类型的I/O操作方式:
- 随机访问,每次读取一个数据块(通过等待事件“db file sequential read”体现出来)。
- 顺序访问,每次读取多个数据块(通过等待事件“db file scattered read”体现出来)。
第一种方式则是访问索引里的数据块,而第二种方式的I/O操作属于全表扫描。这里顺带有一个问题,为何随机访问会对应到db file sequential read等待事件,而顺序访问则会对应到db file scattered read等待事件呢?这似乎反过来了,随机访问才应该是分散(scattered)的,而顺序访问才应该是顺序(sequential)的。其实,等待事件主要根据实际获取物理I/O块的方式来命名的,而不是根据其在I/O子系统的逻辑方式来命名的。下面对于如何获取索引数据块的方式中会对此进行说明。
我们看到前面对B树索引的体系结构的描述,可以知道其为一个树状的立体结构。其对应到数据文件里的
排列当然还是一个平面的形式,也就是像下面这样。因此,当oracle需要访问某个索引块的时候,势必会在这个结构上跳跃的移动。
/根/分支/分支/叶子/…/叶子/分支/叶子/叶子/…/叶子/分支/叶子/叶子/…/叶子/分支/…..
当oracle需要获得一个索引块时,首先从根节点开始,根据所要查找的键值,从而知道其所在的下一层的分支节点,然后访问下一层的分支节点,再次同样根据键值访问再下一层的分支节点,如此这般,最终访问到最底层的叶子节点。可以看出,其获得物理I/O块时,是一个接着一个,按照顺序,串行进行的。在获得最终物理块的过程中,我们不能同时读取多个块,因为我们在没有获得当前块的时候是不知道接下来应该访问哪个块的。因此,在索引上访问数据块时,会对应到db file sequential read等待事件,其根源在于我们是按照顺序从一个索引块跳到另一个索引块,从而找到最终的索引块的。
那么对于全表扫描来说,则不存在访问下一个块之前需要先访问上一个块的情况。全表扫描时,oracle知道要访问所有的数据块,因此唯一的问题就是尽可能高效的访问这些数据块。因此,这时oracle可以采用同步的方式,分几批,同时获取多个数据块。这几批的数据块在物理上可能是分散在表里的,因此其对应到db file scattered read等待事件。