详细解析In-line lob(Basicfile)的物理结构

之前已经写过:

详细解析9i10gdatafile header”

详细解析LMTdatafile的物理结构

详细解析datafilestatus”

详细解析oracle中的transaction”

详细解析truncate引发的object checkpoint”

详细解析ASSMSegment Header的结构

详细解析system回滚段损坏导致的ora-600[4193]错误

 

这里是详细解析系列的第八篇文章,在这篇文章里,我们详细解析了in-line lobBasicfile的物理存储结构和特点。

我们还是从一个实例说起:

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: 0x0040c6aa (1/50858)

scn: 0x0000.80005373 seq: 0x01 flg: 0x02 tail: 0x53730601

frmt: 0x02 chkval: 0x0000 type: 0x06=trans data

Block header dump:  0x0040c6aa

 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 0c 00 00 00 01 00 00 00 01 00 00 00 00 52 6d 0f 8c 09 00 00

 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 Locator00 54 00 01 01 0c 00 00 00 01 00 00 00 01 00 00 00 00 52 6d,长度为20byte

Lob Locator的结构为:

Lob Locator (20 bytes)

{

    len            Lob Locator length       =(2byte) 00 54,这里0x005484,意思是说Lob Locator+Lob Inode+12RDBA,这些全部加来起(即20+16+4*12),长度刚好是84

    version        Version                 =(2byte) 00 01

    lobflg1         Flag 1                  =(1byte) 01,这里0x01表示是blob0x02表示是clob

    lobflg2         Flag 2                  =(1byte) 0c 0x04+0x08

                                     [0x04   index/data is stored as part of disk locator ]

                                     [0x08   locator has been initialized]

    lobflg3         Flag 3                  =(1byte) 00

    lobflg4         unused flag             =(1byte) 00

    bytelength      Char.Set ByteLength     =(2byte) 00 01

    lobid           LobID                   =(10byte) 00 00 00 01 00 00 00 00 52 6d

}

 

2、紧跟着Lob Locator的结构是Lob Inode,这里是0f 8c 09 00 00 00 00 01 07 b8 00 00 00 00 00 01,长度为16byte

Lob Inode的结构为:

Lob Inode (16 bytes)

{

    size_kdlinode   size of inline data     =(2byte) 0f 8c,这里0x0f8c3980,这行blob记录共4000byte,减去Lob Locator20byte,刚好是3980

    flag_kdlinode   flag    =(1byte) 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             =(1byte) 00

    blocks_kdlinode  number of full oracle blocks   =(4byte) 00 00 00 01,这里我认为全部为0更合适,因为这一行blob全部存在行内,不涉及到lobchunk;这里的值为啥为1是因为如果这3964byte的数据存在chunk size2Kchunk里,则需要两个chunk才能存下,且第1chunk一定满了。

    bytes_kdlinode  number of bytes in the last page=(2byte) 07 b8,这里oracle的意思是说如果这3964byte的数据存在chunk size2Kchunk里,则需要两个chunk才能存下,且第1chunk一定满了,第二个chunk的有效byte数是0x07b8

    version_kdlinode  version of LOB          =(6byte) 00 00 00 00 00 01

}

 

3、后面的3964FF就是上述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 0c 00 00 00 01 00 00 00 01 00 00 00 00 52 6e 00 18 05 00 00

 00 00 01 07 b9 00 00 00 00 00 02 03 00 01 f0 03 00 01 f4

注意到这里的第二行记录我是插入了3965byte的数据,那么因为3965+20+164001,已经超过了4000byte,所以oracle这里一定会把这3965byte挪到chunk里的。

3965byte的数据存在chunk size2Kchunk里,则需要两个chunk才能存下,且第1chunk一定满了,第二个chunk的有效byte数一定比第一条记录(3964byte)多了1

从上面我们可以清晰的看到:

Lob Inodenumber of full oracle blocks依然是00 00 00 01

Lob Inodenumber of bytes in the last page07 b9,刚好比第一条记录(第一条记录是07 b8)多了1

这一行数据一共是44byte,减去Lob Locator20byteLob Inode16byte,还剩8byte,即03 00 01 f0 03 00 01 f4

这里Oracle直接在第二行行内存储了那3965byte数据所在的两个chunkRDBA,即0x030001f00x030001f4

我们dump一下这两个块:

Start dump data blocks tsn: 15 file#: 12 minblk 496 maxblk 496

buffer tsn: 15 rdba: 0x030001f0 (12/496)

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: 0x030001f4 (12/500)

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

 

从结果里我们可以看到3965byte确实存储在2chunk里了。

注意,一直到现在为止,上述两条记录都跟lob index没啥关系,lob index一点都没有用到!

我们可以看到这第二条记录的Lob Inodeflag_kdlinode0x050x05=0x01+0x04,意思是说这行记录的lob是有效的且是in-line lob,但lob数据并不完全存储在行内(后面可以看到第三条记录即使用到了lob indexLob Inodeflag_kdlinode依然0x05

注意第一条记录的的Lob Inodeflag_kdlinode0x090x09=0x01+0x08,意思是说这行记录的lob是有效的且能够完全存储在行里(因为不超过4000byte.

 

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 0c 00 00 00 01 00 00 00 01 00 00 00 00 52 6f 00 40 05 00 00

 00 00 10 03 bf 00 00 00 00 00 02 03 00 01 f8 03 00 01 fc 03 00 02 00 03 00

 02 04 03 00 02 08 03 00 00 3a 03 00 00 3e 03 00 00 42 03 00 00 46 03 00 00

 4a 03 00 00 70 03 00 00 74

 

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 50858 maxblk 50858

第三条记录很关键,我插入了32767byte的数据,12chunk size2kchunk是存不下的,所以Oracle这里就真的用到lob index

 

有朋友曾经提到——

就是说即使 Enable storage in rowLOB 字段在LOB的控制信息(即 Locator Inode)长度加 LOB data 长度大于4000 字节的时候,LOB data也是不存储在 row内部的。 在表里 LOB字段的第37-84 字节存储的是 LOB data的前12chunk RDBA 如果12个不够的情况下,这里存放的不是 chunkRDBA,而是LOB index RDBA

注意这里有一个错误的认识——如果12个不够的情况下,这里存放的不是chunkRDBA,而是LOB index RDBA”。

这是不对的。

我们来证明一下,证明的过程是非常容易的,我只需随便从上述12RDBA中取一个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: 00010000526F PageNo       11

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

 

结果已经说明了一切。

所以事实的真相是如果12chunk都已经存不下了,那么Oracle访问头12chunk的时候,只需要直接去行内取这头12chunkRDBA来定位就可以了。

只有在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 ind# from sys.lob$ where obj#=30500;

 

      IND#

———-

     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: 0x3a01 type: 0x06=trans data

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  0x00000000.0000.00  —-    0  fsc 0x0000.00000000

0x02   0x0014.020.0000012f  0x02c00095.0037.0c  –U-    1  fsc 0x0000.80005373

 

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 7c 03 00 00 80 03 00 00 b0 03 00 00 b4 00 00 00 00 00

 00 00 00 00 00 00 00

col 0; len 10; (10):  00 00 00 01 00 00 00 00 52 6f

col 1; len 4; (4):  00 00 00 0c

—– end of leaf block dump —–

End dump data blocks tsn: 15 file#: 12 minblk 555 maxblk 555

 

这里的lob id00 00 00 01 00 00 00 00 52 6f,和第三条记录的lob id匹配。

这个lob index叶子块里存储了5chunkRDBA,我们来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: 0xa78f type: 0x28=PAGETABLE MANAGED LOB BLOCK

Long field block dump:

Object Id    30501

LobId: 00010000526F PageNo       16

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 rowlob(即in-line lobBasicfile的物理存储结构我们已经完全清楚。

 



Leave a Reply

Your email address will not be published. Required fields are marked *