详细解析In-line lob(Basicfile)的物理结构
Posted: December 12, 2011 | Author: Cui Hua | Filed under: Oracle | Tags: basicfile | Leave a comment »之前已经写过:
“详细解析9i和
“详细解析LMT的datafile的物理结构“
“详细解析datafile的status”
“详细解析oracle中的transaction”
“详细解析truncate引发的object checkpoint”
“详细解析ASSM的Segment Header的结构“
“详细解析system回滚段损坏导致的ora-600[4193]错误“
这里是详细解析系列的第八篇文章,在这篇文章里,我们详细解析了in-line lob(Basicfile)的物理存储结构和特点。
我们还是从一个实例说起:
SQL> create table lob_demo
2 (
3 imageindex number,
4 imagecontent blob
5 )
6 lob (imagecontent) store as
7 (tablespace TESTTBS2K
8 enable storage in row
9 chunk 2048
10 cache);
Table created
SQL> declare
2 imagecontent raw(3964);
3 begin
4 imagecontent := utl_raw.cast_to_raw(rpad(‘FF’,3964,’FF’));
5 insert into lob_demo values(1,imagecontent);
6 commit;
7 end;
8 /
PL/SQL procedure successfully completed
SQL> declare
2 imagecontent raw(3965);
3 begin
4 imagecontent := utl_raw.cast_to_raw(rpad(‘FF’,3965,’FF’));
5 insert into lob_demo values(2,imagecontent);
6 commit;
7 end;
8 /
PL/SQL procedure successfully completed
SQL> declare
2 imagecontent raw(32767);
3 begin
4 imagecontent := utl_raw.cast_to_raw(rpad(‘FF’,32767,’FF’));
5 insert into lob_demo values(3,imagecontent);
6 commit;
7 end;
8 /
PL/SQL procedure successfully completed
SQL> select t.*,dbms_rowid.rowid_relative_fno(rowid)||’_’||dbms_rowid.rowid_block_number(rowid) location from lob_demo t;
IMAGEINDEX IMAGECONTENT LOCATION
———- ———— ——————————————————————————–
1 <BLOB> 1_50858
2 <BLOB> 1_50858
3 <BLOB> 1_50858
SQL> select dump(‘FF’,16) from dual;
DUMP(‘FF’,16)
——————-
Typ=96 Len=2: 46,46
Dump一下datafile 1 block 50858,内容如下:
Start dump data blocks tsn: 0 file#: 1 minblk 50858 maxblk 50858
buffer tsn: 0 rdba: 0x
scn: 0x0000.80005373 seq: 0x01 flg: 0x02 tail: 0x53730601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Block header dump: 0x
Object id on Block? Y
seg/obj: 0x7724 csc: 0x00.80005372 itc: 2 flg: O typ: 1 – DATA
fsl: 0 fnx: 0x0 ver: 0x01
……省略显示部分内容
0x12:pri[0] offs=0xff7
0x14:pri[1] offs=0xfc4
0x16:pri[2] offs=0xf69
block_row_dump:
tab 0, row 0, @0xff7
tl: 4009 fb: –H-FL– lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [4000]
00 54 00 01 01
00 00 01 07 b8 00 00 00 00 00 01 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
……省略显示部分内容
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
1、这里Lob Locator为00 54 00 01 01
Lob Locator的结构为:
Lob Locator (20 bytes)
{
len Lob Locator length =(2个byte) 00 54,这里0x0054即84,意思是说Lob Locator+Lob Inode+12个RDBA,这些全部加来起(即20+16+4*12),长度刚好是84
version Version =(2个byte) 00 01
lobflg1 Flag 1 =(1个byte) 01,这里0x01表示是blob,0x02表示是clob
lobflg2 Flag 2 =(1个byte)
[0x04 index/data is stored as part of disk locator ]
[0x08 locator has been initialized]
lobflg3 Flag 3 =(1个byte) 00
lobflg4 unused flag =(1个byte) 00
bytelength Char.Set ByteLength =(2个byte) 00 01
lobid LobID =(10个byte) 00 00 00 01 00 00 00 00 52 6d
}
2、紧跟着Lob Locator的结构是Lob Inode,这里是
Lob Inode的结构为:
Lob Inode (16 bytes)
{
size_kdlinode size of inline data =(2个byte)
flag_kdlinode flag =(1个byte) 09,这里0x09=0x08+0x01
[0x01 This lob is a valid lob ]
[0x02 the inode is in the index ]
[0x04 0x04这里表示是in-line lob]
[0x08 inline data is actual data ]
future_kdlinode free 1 byte =(1个byte) 00
blocks_kdlinode number of full oracle blocks =(4个byte) 00 00 00 01,这里我认为全部为0更合适,因为这一行blob全部存在行内,不涉及到lob的chunk;这里的值为啥为1是因为如果这3964个byte的数据存在chunk size为2K的chunk里,则需要两个chunk才能存下,且第1个chunk一定满了。
bytes_kdlinode number of bytes in the last page=(2个byte) 07 b8,这里oracle的意思是说如果这3964个byte的数据存在chunk size为2K的chunk里,则需要两个chunk才能存下,且第1个chunk一定满了,第二个chunk的有效byte数是0x07b8
version_kdlinode version of LOB =(6个byte) 00 00 00 00 00 01
}
3、后面的3964个FF就是上述blob里实际存储的数据。
tab 0, row 1, @0xfc4
tl: 51 fb: –H-FL– lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [44]
00 54 00 01 01
00 00 01 07 b9 00 00 00 00 00 02 03 00
注意到这里的第二行记录我是插入了3965个byte的数据,那么因为3965+20+16=4001,已经超过了4000个byte,所以oracle这里一定会把这3965个byte挪到chunk里的。
这3965个byte的数据存在chunk size为2K的chunk里,则需要两个chunk才能存下,且第1个chunk一定满了,第二个chunk的有效byte数一定比第一条记录(3964个byte)多了1。
从上面我们可以清晰的看到:
Lob Inode里number of full oracle blocks依然是00 00 00 01
Lob Inode里number of bytes in the last page是07 b9,刚好比第一条记录(第一条记录是07 b8)多了1。
这一行数据一共是44个byte,减去Lob Locator的20个byte和Lob Inode的16个byte,还剩8个byte,即03 00
这里Oracle直接在第二行行内存储了那3965个byte数据所在的两个chunk的RDBA,即0x
我们dump一下这两个块:
Start dump data blocks tsn: 15 file#: 12 minblk 496 maxblk 496
buffer tsn: 15 rdba: 0x
scn: 0x0000.8000536e seq: 0x02 flg: 0x04 tail: 0x536e2802
frmt: 0x02 chkval: 0xa697 type: 0x28=PAGETABLE MANAGED LOB BLOCK
Long field block dump:
Object Id 30501
LobId: 00010000526E PageNo 0
Version: 0x0000.00000001 pdba: 50331688
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
……省略显示部分内容
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
Start dump data blocks tsn: 15 file#: 12 minblk 500 maxblk 500
buffer tsn: 15 rdba: 0x
scn: 0x0000.8000536e seq: 0x02 flg: 0x04 tail: 0x536e2802
frmt: 0x02 chkval: 0xe092 type: 0x28=PAGETABLE MANAGED LOB BLOCK
Long field block dump:
Object Id 30501
LobId: 00010000526E PageNo 1
Version: 0x0000.00000001 pdba: 50331688
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
……省略显示部分内容
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 00 00 00 00 00 00 00 00 00 00 00
从结果里我们可以看到,这3965个byte确实存储在2个chunk里了。
注意,一直到现在为止,上述两条记录都跟lob index没啥关系,lob index一点都没有用到!
我们可以看到这第二条记录的Lob Inode的flag_kdlinode是0x05,0x05=0x01+0x04,意思是说这行记录的lob是有效的且是in-line lob,但lob数据并不完全存储在行内(后面可以看到第三条记录即使用到了lob index,Lob Inode的flag_kdlinode依然是0x05)。
注意第一条记录的的Lob Inode的flag_kdlinode是0x09,0x09=0x01+0x08,意思是说这行记录的lob是有效的且能够完全存储在行里(因为不超过4000个byte).
tab 0, row 2, @0xf69
tl: 91 fb: –H-FL– lb: 0x1 cc: 2
col 0: [ 2] c1 04
col 1: [84]
00 54 00 01 01
00 00 10 03 bf 00 00 00 00 00 02 03 00
02 04 03 00 02 08 03 00 00
end_of_block_dump
End dump data blocks tsn: 0 file#: 1 minblk 50858 maxblk 50858
第三条记录很关键,我插入了32767个byte的数据,12个chunk size为2k的chunk是存不下的,所以Oracle这里就真的用到lob index了。
有朋友曾经提到——
就是说即使 Enable storage in row,LOB 字段在LOB的控制信息(即 Locator 加Inode)长度加 LOB data 长度大于4000 字节的时候,LOB data也是不存储在 row内部的。 在表里 LOB字段的第37-84 字节存储的是 LOB data的前12个chunk 的RDBA。 如果12个不够的情况下,这里存放的不是 chunk的RDBA,而是LOB index的 RDBA。
注意这里有一个错误的认识——“如果12个不够的情况下,这里存放的不是chunk的RDBA,而是LOB index的 RDBA”。
这是不对的。
我们来证明一下,证明的过程是非常容易的,我只需随便从上述12个RDBA中取一个dump一下,dump的结果就可以说明一切。
好了,我们现在来dump最后一个RDBA ( 03 00 00 74)
Start dump data blocks tsn: 15 file#: 12 minblk 116 maxblk 116
buffer tsn: 15 rdba: 0x03000074 (12/116)
scn: 0x0000.80005372 seq: 0x02 flg: 0x04 tail: 0x53722802
frmt: 0x02 chkval: 0xa713 type: 0x28=PAGETABLE MANAGED LOB BLOCK
Long field block dump:
Object Id 30501
LobId:
Version: 0x0000.00000001 pdba: 50331682
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
……省略显示部分内容
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
End dump data blocks tsn: 15 file#: 12 minblk 116 maxblk 116
结果已经说明了一切。
所以事实的真相是如果12个chunk都已经存不下了,那么Oracle访问头12个chunk的时候,只需要直接去行内取这头12个chunk的RDBA来定位就可以了。
只有在Oracle需要去访问第13个及其以后的chunk的时候,才需要lob index的帮忙。
这里借助lob index帮忙的过程是很容易搞清楚的,我们只需要做一个tree dump就可以了。
SQL> select object_id from dba_objects where object_name=’LOB_DEMO’;
OBJECT_ID
———-
30500
SQL> select
———-
30502
alter session set events ‘immediate trace name treedump level 30502’;
dump的结果如下:
—– begin tree dump
leaf: 0x300022b 50332203 (0: nrow: 1 rrow: 1)
—– end tree dump
Start dump data blocks tsn: 15 file#: 12 minblk 555 maxblk 555
buffer tsn: 15 rdba: 0x0300022b (12/555)
scn: 0x0000.80005373 seq: 0x01 flg: 0x06 tail: 0x53730601
frmt: 0x02 chkval: 0x
Block header dump: 0x0300022b
Object id on Block? Y
seg/obj: 0x7726 csc: 0x00.800047b7 itc: 2 flg: E typ: 2 – INDEX
brn: 0 bdba: 0x3000221 ver: 0x01
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.000.00000000 0x0000
0x02 0x0014.
Leaf block dump
===============
header address 4565971044=0x110273064
kdxcolev 0
KDXCOLEV Flags = – – –
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=— is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 1
kdxcofbo 38=0x26
kdxcofeo 1838=0x72e
kdxcoavs 1800
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 32
kdxlebksz 1888
row#0[1838] flag: —–, lock: 2, data:(32):
03 00 00 78 03 00 00
00 00 00 00 00 00 00
col 0; len 10; (10): 00 00 00 01 00 00 00 00 52
col 1; len 4; (4): 00 00 00
—– end of leaf block dump —–
End dump data blocks tsn: 15 file#: 12 minblk 555 maxblk 555
这里的lob id是00 00 00 01 00 00 00 00 52
这个lob index叶子块里存储了5个chunk的RDBA,我们来dump最后一个(即03 00 00 b4)
Start dump data blocks tsn: 15 file#: 12 minblk 180 maxblk 180
buffer tsn: 15 rdba: 0x030000b4 (12/180)
scn: 0x0000.80005372 seq: 0x02 flg: 0x04 tail: 0x53722802
frmt: 0x02 chkval: 0xa
Long field block dump:
Object Id 30501
LobId:
Version: 0x0000.00000001 pdba: 50331683
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46
……省略显示部分内容
46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
……省略显示部分内容
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
End dump data blocks tsn: 15 file#: 12 minblk 180 maxblk 180
至此,这种enable storage in row的lob(即in-line lob,Basicfile)的物理存储结构我们已经完全清楚。
Recent Comments