SQL> SELECT VALUE FROM V$DIAG_INFO WHERE NAME = "Default Trace File";
VALUE
-------------------------------------------------------------------------------
F:\ORACLE\PRODUCT\10.2.0\ADMIN\ORCL\UDUMP\orcl_ora_10076.trc
既然已经找到了转储文件,那就看看转储文件的内容:
Start dump data blocks tsn: 7 file#: 6 minblk 41 maxblk 41 --转储的文件号和块号
buffer tsn: 7 rdba: 0x01800029 (6/41)
scn: 0x0000.000bddf9 seq: 0x01 flg: 0x04 tail: 0xddf92001
frmt: 0x02 chkval: 0x4b0e type: 0x20=FIRST LEVEL BITMAP BLOCK --这个表示它是一级位图块,直接和数据块打交道
Hex dump of block: st=0, typ_found=1
Dump of First Level Bitmap Block
--------------------------------
nbits : 2 nranges: 1 parent dba: 0x0180002a poffset: 0 --标红的表示它的父级位图块
unformatted: 12 total: 16 first useful block: 3
owning instance : 1
instance ownership changed at 04/20/2013 12:08:33
Last successful Search 04/20/2013 12:08:33
Freeness Status: nf1 0 nf2 1 nf3 0 nf4 0
Extent Map Block Offset: 4294967295
First free datablock : 3
Bitmap block lock opcode 0
Locker xid: : 0x0000.000.00000000
Inc #: 0 Objd: 52590
HWM Flag: HWM Set
Highwater:: 0x0180002d ext#: 0 blk#: 4 ext size: 16
#blocks in seg. hdr's freelists: 0
#blocks below: 1
mapblk 0x00000000 offset: 0
--------------------------------------------------------
DBA Ranges :
--------------------------------------------------------
0x01800029 Length: 16 Offset: 0
0:Metadata 1:Metadata 2:Metadata 3:25-50% free
4:unformatted 5:unformatted 6:unformatted 7:unformatted
8:unformatted 9:unformatted 10:unformatted 11:unformatted
12:unformatted 13:unformatted 14:unformatted 15:unformatted
--------------------------------------------------------
End dump data blocks tsn: 7 file#: 6 minblk 41 maxblk 41
-标红的这些块被用来存储元数据,其余的块用来存储数据,注意到总共有16个块,full表示数据块已经用完了。
如果这些块用完的话,oracle就会使用二级位图结构,以此类推,会出现三级位图结构,oracle目前支持到三级位图结构,上面的表示这些块暂时未被使用,根据以上的说明oracle会从第4个块(44)开始存放索引,索引的branch_code应该放在第44块中,接下来我们就分析索引是如何分布的。
下面是一个rdba转换为具体的文件和块号的方法,后面会用到。
create or replace function getbfno(p_dba in varchar2)
return varchar2
is
l_str varchar2(255) default null;
begin
l_str :='datafile# is:'
||dbms_utility.data_block_address_file(TO_NUMBER(LTRIM(P_DBA,'0x'),'xxxxxxxx'))
||chr(10) ||'datablock is:'
||dbms_utility.data_block_address_block(TO_NUMBER(LTRIM(P_DBA,'0x'),'xxxxxxxx'));
return l_str;
end;
--去上面的rdba得到如下的转换
SQL> select getbfno('0x01800029') from dual;
GETBFNO('0X01800029')
---------------------------------------------
datafile# is:6
datablock is:41
在开始插入索引前,我们下去看看 parent dba: 0x0180002a 这个里面有什么东西,也就是是二级位图块。
SQL> select getbfno('0x0180002a') from dual;
GETBFNO('0X0180002A')
-------------------------------------------------
datafile# is:6
datablock is:42
我们看到二级位图块是第42块,转储42块得到以下内容
*** 2013-04-20 15:30:52.140
*** SERVICE NAME:(SYS$USERS) 2013-04-20 15:30:52.125
*** SESSION ID:(121.797) 2013-04-20 15:30:52.125
Start dump data blocks tsn: 7 file#: 6 minblk 42 maxblk 42
buffer tsn: 7 rdba: 0x0180002a (6/42)
scn: 0x0000.000bddf2 seq: 0x01 flg: 0x04 tail: 0xddf22101
frmt: 0x02 chkval: 0x4be9 type: 0x21=SECOND LEVEL BITMAP BLOCK --表示它是二级位图
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x09142200 to 0x09144200
9142200 0000A221 0180002A 000BDDF2 04010000 [!...*...........]
9142210 00004BE9 00000000 00000000 00000000 [.K..............]
9142220 00000000 00000000 00000000 00000000 [................]
Repeat 1 times
9142240 00000000 00000000 00000000 0180002B [............+...]
9142250 00000001 00000001 00000000 00000000 [................]
9142260 00000000 00000000 0000CD6E 00000001 [........n.......]
9142270 00000000 01800029 00010005 00000000 [....)...........]
9142280 00000000 00000000 00000000 00000000 [................]
Repeat 502 times
91441F0 00000000 00000000 00000000 DDF22101 [.............!..]
Dump of Second Level Bitmap Block
number: 1 nfree: 1 ffree: 0 pdba: 0x0180002b --此处它指向了43块
Inc #: 0 Objd: 52590
opcode:0
xid:
L1 Ranges :
--------------------------------------------------------
0x01800029 Free: 5 Inst: 1 --在此处它指向了25块,
这一块用来记录那些位图块管理单元被包含在对象中
--------------------------------------------------------
End dump data blocks tsn: 7 file#: 6 minblk 42 maxblk 42
在上面看到一块,其中pdba指向了43块,不明白是做什么的,顺便看一下
Dump of Second Level Bitmap Block
number: 1 nfree: 1 ffree: 0 pdba: 0x0180002b --此处它指向了43块
Inc #: 0 Objd: 52590
opcode:0
43块的内容经过转储内容如下:(中间省略了一部分省略了)
*** 2013-04-20 15:40:25.406
Start dump data blocks tsn: 7 file#: 6 minblk 43 maxblk 43
buffer tsn: 7 rdba: 0x0180002b (6/43)
scn: 0x0000.000bddf9 seq: 0x01 flg: 0x04 tail: 0xddf92301
frmt: 0x02 chkval: 0x66e9 type: 0x23=PAGETABLE SEGMENT HEADER
Hex dump of block: st=0, typ_found=1
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 1 #blocks: 16
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x0180002d ext#: 0 blk#: 4 ext size: 16
#blocks in seg. hdr's freelists: 0
#blocks below: 1
mapblk 0x00000000 offset: 0
Unlocked
--------------------------------------------------------
Low HighWater Mark :
Highwater:: 0x0180002d ext#: 0 blk#: 4 ext size: 16
#blocks in seg. hdr's freelists: 0
#blocks below: 1
mapblk 0x00000000 offset: 0
Level 1 BMB for High HWM block: 0x01800029 --此处标注了高低水位线
Level 1 BMB for Low HWM block: 0x01800029
--------------------------------------------------------
Segment Type: 2 nl2: 1 blksz: 8192 fbsz: 0
L2 Array start offset: 0x00001434
First Level 3 BMB: 0x00000000
L2 Hint for inserts: 0x0180002a
Last Level 1 BMB: 0x01800029
Last Level II BMB: 0x0180002a
Last Level III BMB: 0x00000000
Map Header:: next 0x00000000 #extents: 1 obj#: 52590 flag: 0x10000000
Inc # 0
Extent Map
-----------------------------------------------------------------
0x01800029 length: 16
Auxillary Map
--------------------------------------------------------
Extent 0 : L1 dba: 0x01800029 Data dba: 0x0180002c
--如果已经有空间被使用,则会指出该空间的第一个数据块的位置,可以通过此处找到branch_code
--------------------------------------------------------
Second Level Bitmap block DBAs --此处记录了二级位图的地址
--------------------------------------------------------
DBA 1: 0x0180002a
End dump data blocks tsn: 7 file#: 6 minblk 43 maxblk 43
Block header dump: 0x0180002c
Object id on Block? Y
seg/obj: 0xcd6e csc: 0x00.c43b8 itc: 1 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x1800029 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0004.018.00000189 0x00800639.0165.02 C--- 0 scn 0x0000.000c43b7
Branch block dump
=================
header address 152314444=0x914224c
kdxcolev 1
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 1
kdxconro 18
kdxcofbo 64=0x40
kdxcofeo 7898=0x1eda
kdxcoavs 7834
kdxbrlmc 25165871=0x180002f
kdxbrsno 17
kdxbrbksz 8060
kdxbr2urrc 0
row#0[8051] dba: 25165872=0x1800030 --从这可以看到叶子节点的存储位置
col 0; len 3; (3): c2 06 2a
col 1; TERM
row#1[8042] dba: 25165873=0x1800031
col 0; len 3; (3): c2 0b 4b
col 1; TERM
row#2[8033] dba: 25165874=0x1800032
col 0; len 3; (3): c2 11 08
col 1; TERM
row#3[8024] dba: 25165875=0x1800033
col 0; len 3; (3): c2 16 29
col 1; TERM
row#4[8015] dba: 25165876=0x1800034
col 0; len 3; (3): c2 1b 4a
col 1; TERM
row#5[8006] dba: 25165877=0x1800035
col 0; len 3; (3): c2 21 07
col 1; TERM
row#6[7997] dba: 25165878=0x1800036
col 0; len 3; (3): c2 26 28
col 1; TERM
看到这儿要了解一下dump这个函数
dump 函数能查看表中列在datafile存储内容。
Oracle的NUMBER类型最多由三个部分构成,这三个部分分别是最高位表示位、数据部分、符号位。其中负数包含符号位,正数不会包括符号位(10进制即102)。另外,数值0比较特殊,它只包含一个数值最高位表示位80(16进制),没有数据部分。
DUMP函数的输出格式类似:
类型 <[长度]>,符号/指数位 [数字1,数字2,数字3,......,数字20]
各位的含义如下:
1.类型: Number型,Type=2 (类型代码可以从Oracle的文档上查到)
2.长度:指存储的字节数
3.符号/指数位
在存储上,Oracle对正数和负数分别进行存储转换:
正数:加1存储(为了避免Null)
负数:被101减,如果总长度小于21个字节,最后加一个102(是为了排序的需要)
指数位换算:
正数:指数=符号/指数位 - 193 (最高位为1是代表正数)
负数:指数=62 - 第一字节
4.从<数字1>开始是有效的数据位
从<数字1>开始是最高有效位,所存储的数值计算方法为:
将下面计算的结果加起来:
每个<数字位>乘以100^(指数-N) (N是有效位数的顺序位,第一个有效位的N=0)
举例说明
SQL> select dump(123456.789) from dual; DUMP(123456.789)
-------------------------------
Typ=2 Len=6: 195,13,35,57,79,91 |
<指数>: 195 - 193 = 2
<数字1> 13 - 1 = 12 *100^(2-0) 120000
<数字2> 35 - 1 = 34 *100^(2-1) 3400
<数字3> 57 - 1 = 56 *100^(2-2) 56
<数字4> 79 - 1 = 78 *100^(2-3) .78
<数字5> 91 - 1 = 90 *100^(2-4) .009
123456.789
SQL> select dump(-123456.789) from dual; DUMP(-123456.789)
----------------------------------
Typ=2 Len=7: 60,89,67,45,23,11,102 |
<指数> 62 - 60 = 2(最高位是0,代表为负数)
<数字1> 101 - 89 = 12 *100^(2-0) 120000
<数字2> 101 - 67 = 34 *100^(2-1) 3400
<数字3> 101 - 45 = 56 *100^(2-2) 56
<数字4> 101 - 23 = 78 *100^(2-3) .78
<数字5> 101 - 11 = 90 *100^(2-4) .009
123456.789(-)
现在我们查看叶子节点
SQL> select getbfno('0x1800030') from dual;
GETBFNO('0X1800030')
-----------------------------------------------
datafile# is:6
datablock is:48
转储一下48号数据块可以看到:
Block header dump: 0x01800030
Object id on Block? Y
seg/obj: 0xcd6e csc: 0x00.c16d6 itc: 2 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x1800029 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0001.026.00000179 0x00800012.00cf.01 -BU- 1 fsc 0x0000.000c16d9
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
Leaf block dump
===============
header address 152314468=0x9142264
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 1
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 2
kdxconro 533
kdxcofbo 1102=0x44e
kdxcofeo 1112=0x458
kdxcoavs 10
kdxlespl 0
kdxlende 0
kdxlenxt 25165873=0x1800031
kdxleprv 25165871=0x180002f
kdxledsz 0
kdxlebksz 8036
row#0[1112] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2a
col 1; len 6; (6): 01 80 00 1c 02 1c
row#1[1125] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2b
col 1; len 6; (6): 01 80 00 1c 02 1d
row#2[1138] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2c
col 1; len 6; (6): 01 80 00 1c 02 1e
row#3[1151] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2d
col 1; len 6; (6): 01 80 00 1c 02 1f
row#4[1164] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2e
col 1; len 6; (6): 01 80 00 1c 02 20后面省略
注意到以下:
row#0[8051] dba: 25165872=0x1800030 --从这可以看到叶子节点的存储位置
col 0; len 3; (3): c2 06 2a --541
col 1; TERM
row#0[1112] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2a --541
col 1; len 6; (6): 01 80 00 1c 02 1c --rowid
分支节点存放了第一个叶子节点的值,这样在进行范围判断时候直接使用叶子节点中的值来判断值位于哪一个块中。
删除541这一行数据
row#0[1112] flag: ---D--, lock: 2, len=13 --表示数据已经被删除
col 0; len 3; (3): c2 06 2a
col 1; len 6; (6): 01 80 00 1c 02 1c
row#0[8051] dba: 25165872=0x1800030
col 0; len 3; (3): c2 06 2a
col 1; TERM
发现branch_code并没有删除,还是指向了它,并且在叶子节点并没有删除它,所以当DELETE 和 insert 很频繁的时候,索引会越来越庞大。
因为使用DELETE删除数据并不会删除索引,只会标记为索引无效。
下面我们来看这一行
row#1[1125] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2b -- 542
col 1; len 6; (6): 01 80 00 1c 02 1d
我们来更新这一行 ,将他的值变为545 看看索引会发生什么变化。
SQL> update test set id='545' where id='542';
已更新 1 行。
row#3[1112] flag: ------, lock: 2, len=13
col 0; len 3; (3): c2 06 2e
col 1; len 6; (6): 01 80 00 1c 02 1d
rowid是完全一样的,同时发现一个问题
前面删除的哪一行也不在了,541哪一行的索引没有在出现,难道使用UPADATE 运算会删除以前被删除了的索引,这个是个疑问,在去试一下。好像是这样的,这个问题还有待研究。
Leaf block dump
===============
header address 152314468=0x9142264
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 2
kdxconro 533
kdxcofbo 1102=0x44e
kdxcofeo 1112=0x458
kdxcoavs 10
kdxlespl 0
kdxlende 1
kdxlenxt 25165873=0x1800031
kdxleprv 25165871=0x180002f
kdxledsz 0
kdxlebksz 8036
row#0[8023] flag: ---D--, lock: 2, len=13 --头变成这样了
col 0; len 3; (3): c2 06 2c --这一行被我update掉了,但是前面刚刚废弃的索引不见了。
col 1; len 6; (6): 01 80 00 1c 02 1e
row#1[8010] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2d
col 1; len 6; (6): 01 80 00 1c 02 1f
row#2[7997] flag: ------, lock: 0, len=13
col 0; len 3; (3): c2 06 2e
这个问题希望大家共同探讨。。。。
索引的update和delete操作就是这样的,下面我们来做索引的分裂实验。
SQL> create table test2(id number,name varchar2(100));
表已创建。
SQL> create index idx_test2 on test2(id);
索引已创建。
SQL> declare
2 begin
3 for i in 1..540
4
5 loop
6
7 insert into test2 values (i,'100');
8 commit;
9 end loop ;
10
11 end;
12
13 /
SQL> insert into test2 values(5,'100');
已创建 1 行。
发生了分裂
Block header dump: 0x0180006c
Object id on Block? Y
seg/obj: 0xcd7e csc: 0x00.c9fb3 itc: 1 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x1800069 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000a.017.00000194 0x008000a0.011a.01 -BU- 1 fsc 0x0000.000c9fb5
Branch block dump
=================
header address 152314444=0x914224c
kdxcolev 1
KDXCOLEV Flags = - - -
kdxcolok 1
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 1
kdxconro 1
kdxcofbo 30=0x1e
kdxcofeo 8051=0x1f73
kdxcoavs 8021
kdxbrlmc 25165935=0x180006f
kdxbrsno 0
kdxbrbksz 8060
kdxbr2urrc 0
row#0[8051] dba: 25165936=0x1800070
col 0; len 3; (3): c2 03 50 --349
col 1; TERM
----- end of branch block dump -----
End dump data blocks tsn: 7 file#: 6 minblk 108 maxblk 108
SQL> select getbfno('0x1800070') from dual;
GETBFNO('0X1800070')
-----------------------------------------------
datafile# is:6
datablock is:112
此时我们分别转储111和112数据文件
111文件如下
Leaf block dump
===============
header address 152314468=0x9142264
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 1
kdxconro 279
kdxcofbo 594=0x252
kdxcofeo 4499=0x1193
kdxcoavs 3917
kdxlespl 0
kdxlende 0
kdxlenxt 25165936=0x1800070
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[4511] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 02
col 1; len 6; (6): 01 80 00 5c 00 00
row#1[4523] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 03
col 1; len 6; (6): 01 80 00 5c 00 01
row#2[4535] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 04
col 1; len 6; (6): 01 80 00 5c 00 02
row#3[4547] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 05
col 1; len 6; (6): 01 80 00 5c 00 03
row#4[4559] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 06 --引起了拆分
col 1; len 6; (6): 01 80 00 5c 00 04
row#5[4499] flag: ------, lock: 2, len=12
col 0; len 2; (2): c1 06
col 1; len 6; (6): 01 80 00 5c 02 1c
row#6[4583] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 07
col 1; len 6; (6): 01 80 00 5c 00 05
row#7[4595] flag: ------, lock: 0, len=12
col 0; len 2; (2): c1 08
。。。。。。。