Data Block Structure (Try to Understand...)

虽然知道可以通过alter system dump datafile xxx block xxx来转储block的内容,但是遗憾的是对生成的trace文件内容却是无从下手,真的跟看天书一般。偶然看到有些牛人做了些研究,而且分享了出来,参见这里, 于是欣欣然尝试去读读这些”神秘代码“,可是费了半天劲还是只是搞懂了些皮毛而已。先记录下来,以后有机会接着看看...

SQL> select * from v$version where rownum=1;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod

SQL
> create smallfile tablespace small_tbs datafile '/u01/app/oracle/oradata/orcl/small_tbs01.dbf' size 10m autoextend on;

Tablespace created.

SQL
> create table small_table(id number, name varchar2(10)) tablespace small_tbs;

Table created.

SQL
> insert into small_table values(1, 'Frank');

1 row created.


SQL
> commit;

Commit complete.

SQL
> select rowid from small_table;

ROWID
------------------
AAAM2eAAGAAAAANAAA



SQL
>

SQL
> select rowid
, substr(rowid,
1, 6) object_id_section
, dbms_rowid.rowid_object(rowid)
object_id
, substr(rowid,
7, 3) file_no_section
, dbms_rowid.rowid_relative_fno(rowid,
'SMALLFILE') relative_file_no
, substr(rowid,
10, 6) block_no_section
, dbms_rowid.rowid_block_number(rowid,
'SMALLFILE') block_no
, substr(rowid,
16, 3) row_no_section
, dbms_rowid.rowid_row_number(rowid) row_no
, dbms_rowid.rowid_to_restricted(rowid,
0) internal_restriced_rowid
, dbms_rowid.rowid_to_restricted(rowid,
1) external_restriced_rowid
from small_table;

ROWID OBJECT_ID_SEC
OBJECT_ID FILE_NO_SECT RELATIVE_FILE_NO BLOCK_NO_SECT BLOCK_NO ROW_NO_SECTI ROW_NO INTERNAL_RESTRICED EXTERNAL_RESTRICED
------------------ ------------- ---------- ------------ ---------------- ------------- --------- ------------ ------- ------------------ ------------------
AAAM2eAAGAAAAANAAA AAAM2e 52638 AAG 6 AAAAAN 13 AAA 0 0000000D.0000.0006 0000000D.0000.0006

-- dump the block
SQL
> alter system dump datafile 6 block 13;

好吧,生成的block dump文件内容如下...

*** 2011-01-25 22:12:33.639
Start
dump data blocks tsn: 10 file#: 6 minblk 13 maxblk 13
buffer tsn:
10 rdba: 0x0180000d (6/13)
scn:
0x0000.000a9c4a seq: 0x01 flg: 0x06 tail: 0x9c4a0601
frmt:
0x02 chkval: 0xfcd4 type: 0x06=trans data
Hex
dump of block: st=0, typ_found=1
Dump of memory from 0x0E4F3800 to 0x0E4F5800
E4F3800 0000A206 0180000D 000A9C4A
06010000 [........J.......]
E4F3810 0000FCD4
00000001 0000CD9E 000A9C45 [............E...]
E4F3820
00000000 00320002 01800009 00110008 [......2.........]
E4F3830 0000015F 008000BB 0032012F
00002001 [_......./.2.. ..]
E4F3840 000A9C4A
00000000 00000000 00000000 [J...............]
E4F3850
00000000 00000000 00000000 00000000 [................]
E4F3860
00000000 00010100 0014FFFF 1F781F8C [..............x.]
E4F3870 00001F78 1F8C0001
00000000 00000000 [x...............]
E4F3880
00000000 00000000 00000000 00000000 [................]
Repeat
502 times
E4F57F0 0202012C 460502C1 6B6E6172 9C4A0601
[,......Frank..J.]
Block header
dump: 0x0180000d
Object id
on Block? Y
seg
/obj: 0xcd9e csc: 0x00.a9c45 itc: 2 flg: E typ: 1 - DATA
brn:
0 bdba: 0x1800009 ver: 0x01 opc: 0
inc:
0 exflg: 0
Itl Xid Uba Flag Lck Scn
/Fsc
0x01 0x0008.011.0000015f 0x008000bb.012f.32 --U- 1 fsc 0x0000.000a9c4a
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
data_block_dump,data header at 0xe4f3864
===============
tsiz:
0x1f98
hsiz:
0x14
pbl:
0x0e4f3864
bdba:
0x0180000d
76543210
flag
=--------
ntab=1
nrow
=1
frre
=-1
fsbo
=0x14
fseo
=0x1f8c
avsp
=0x1f78
tosp
=0x1f78
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f8c
block_row_dump:
tab
0, row 0, @0x1f8c
tl:
12 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col
1: [ 5] 46 72 61 6e 6b
end_of_block_dump
End dump data blocks tsn: 10 file#: 6 minblk 13 maxblk 13

看了之前提到的别人的研究分享,尝试把上面这个dump文件内容进行标记标记下,于是费了九牛二虎之力用QQ截图工具加工了下,如下,

因为我dump的这个block是个存储数据的块,可以从块头的标志位06 (type:trans data)看出来,后来我又dump了一个segment header block,内容跟这个block内容还是有很大差别的。这个图我只是标了一部分,没标记的是没看懂的 :( 

我很好奇的是最后一行的前12个字节,

0202012C 460502C1 6B6E6172


这一行很显然应该是对应于表small_table中的一行数据(1, 'FRANK'), 从dump文件的最后一部分可以看出来,

col 0: [ 2] c1 02
col
1: [ 5] 46 72 61 6e 6b

不过有点奇怪的是,这个顺序不一样,难道是因为什么big/little endian的问题?查看下操作系统的字节顺序,发现是Little Endian的. 

God....这个可真是恶,难怪不好理解!


SQL> select tp.platform_name, tp.endian_format
2 from v$transportable_platform tp,
3 v$database d
4 where tp.platform_name = d.platform_name;


PLATFORM_NAME ENDIAN_FORMAT
--------------------------- --------------
Linux IA (32-bit) Little

XXXXXXXX 4605XXXX 6B6E6172 -- 应该对应'FRANK', 05应该是这个列的长度,占用5个字节。剩下的46, 6b, 6e, 61, 72应该分别对应'F', 'k', 'n', 'a', 'r'

02XXXXXX XXXX02C1 XXXXXXXX -- 应该对应数字1, 第一个02代表占用的字节数,02c1 (正常是c1, 02)

那么剩下的

XX02012C XXXXXXXX XXXXXXXX

这个表示嘛呢???


另外还有一点疑问的是对于rdba (relative data block address)

rdba: 0x0180000d (6/13)

从括号里面的信息可以看出来表示的是file 6, block 13. 那么这个是怎么从0x0180000d推算出来的呢?看到有人是这么算的....

SQL> select to_number('018', 'xxx')/4 fno, to_number('0000d' ,'xxxxx') blockno from dual;

FNO BLOCKNO
---------- ----------
6 13

为啥这么算呢??难道是这样.....?


了解到对于smallfile的表空间来说,ROWID (OOOOOOFFFBBBBBBRRR)中用10个bit来存储文件号,12个bit来存储block number,一共是32个bit, 4个字节。那么来

看看这个0X0180000d,这个也是4个字节,前10个bit是file number, 但是018是12个bit,因此去018必然多算了最后的两个bit,因此需要右移两位来得到实际的file number,

因此也就是除以4 -- to_number('018', 'xxx')/4

不过也可以更直观地把018,换成二进制如下....

0000 0001 1000

取前10个bit, 为0000000110, 正好也就是6! 至于后面的block number, 因为高位都是0,因此可以忽略,直接把d换成10进制13即可。




///////////////////////////////////////////////

<--to be continued-->

Update when there is new finding...

///////////////////////////////////////////////












--------------------------------------
Regards,
FangwenYu
原文地址:https://www.cnblogs.com/fangwenyu/p/1946462.html