yangtingkun
===========================================================
行链接在存储结构上是如何实现的(一)
===========================================================

如果一条记录的长度超过了BLOCK_SIZEOracle会以行链接的形式来存放,不过Oracle到底是如何实现行链接的,一直没有深究,直到有一个网友问起这个问题。


如果知道答案,这个问题并不困难,所以如果只是将结果贴出来可能意义并不大,因此这里将寻找答案的整个探索过程贴出来,希望能起到抛砖引玉的作用。

首先将问题描述的清晰一些,如果表中的某一条记录的长度超过了DB_BLOCK_SIZE的大小,则Oracle没有办法在一个BLOCK中存放这条记录。显然这条记录会存放到多个BLOCK中,而且出于空间利用的考虑,Oracle在将记录存放到多个BLOCK的时候,并不会保证每列都完整的放在一个BLOCK上,因为VARCHAR2(4000)列或LONG列本身可能会大于1BLOCK的大小。

可以推断,Oracle尽可能在每个BLOCK中写满数据,写不下的数据会写到另外的BLOCK中:

SQL> create table t
2 (col1 varchar2(4000),
3 col2 varchar2(4000),
4 col3 varchar2(4000))
5 tablespace test;

表已创建。

SQL> insert into t values
2 (lpad('1', 4000, '1'),
3 lpad('2', 4000, '2'),
4 lpad('3', 4000, '3'));

已创建 1 行。

SQL> alter system checkpoint;

系统已更改。

SQL> SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID), DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) FROM T;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------ ------------------------------------
28 11

SQL> ALTER SYSTEM DUMP DATAFILE 28 BLOCK 11;

系统已更改。

下面看看对应的DUMP文件信息:

buffer tsn: 27 rdba: 0x0700000b (28/11)
scn: 0x0001.78a8fb01 seq: 0x05 flg: 0x04 tail: 0xfb010605
frmt: 0x02 chkval: 0xccd4 type: 0x06=trans data
Block header dump: 0x0700000b
Object id on Block? Y
seg/obj: 0x98e9 csc: 0x01.78a8fb01 itc: 2 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0003.010.00017778 0x00915927.23a1.48 ---- 1 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000

data_block_dump,data header at 0xb742d25c
===============
tsiz: 0x1fa0
hsiz: 0x14
pbl: 0xb742d25c
bdba: 0x0700000b
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0xfe1
avsp=0xfcd
tosp=0xfcd
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0xfe1
block_row_dump:
tab 0, row 0, @0xfe1
tl: 4031 fb: --H-F--N lb: 0x1 cc: 2
nrid: 0x0700000a.0col 0: [4000]
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
.
.
.
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
col 1: [18] 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
end_of_block_dump
End dump data blocks tsn: 27 file#: 28 minblk 11 maxblk 11

可以看到,这个BLOCK并没有在整列的时候中断,而是在这个块中写了部分COL2的数据。这就证实文章开头的推测。

从上面红色的地址可以找到下一个BLOCK

SQL> select dump(rowid, 16) from t;

DUMP(ROWID,16)
------------------------------------------------------------------------
Typ=69 Len=10: 0,0,98,e9,7,0,0,b,0,0

SQL> ALTER SYSTEM DUMP DATAFILE 28 BLOCK 10;

系统已更改。

无论是和当前记录的ROWID对比,还是根据ROWID的存储格式特点,都可以确定下一个BLOCK的位置,对这个BLOCK进行DUMP,再次观察:

Start dump data blocks tsn: 27 file#: 28 minblk 10 maxblk 10
buffer tsn: 27 rdba: 0x0700000a (28/10)
scn: 0x0001.78a8fb01 seq: 0x06 flg: 0x04 tail: 0xfb010606
frmt: 0x02 chkval: 0x5e2d type: 0x06=trans data
Block header dump: 0x0700000a
Object id on Block? Y
seg/obj: 0x98e9 csc: 0x01.78a8fb01 itc: 3 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0003.010.00017778 0x00915927.23a1.47 ---- 1 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.00000000

data_block_dump,data header at 0xb742d274
===============
tsiz: 0x1f88
hsiz: 0x14
pbl: 0xb742d274
bdba: 0x0700000a
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x51
avsp=0x3d
tosp=0x3d
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x51
block_row_dump:
tab 0, row 0, @0x51
tl: 7991 fb: -----LP- lb: 0x1 cc: 2
col 0: [3982]
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
.
.
.
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32
col 1: [4000]
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
.
.
.
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
end_of_block_dump
End dump data blocks tsn: 27 file#: 28 minblk 10 maxblk 10

看完这个BLOCK的结果,可以发现Oracle似乎是从后向前进行的写操作,之所以这样说是因为这个块被写满了,而前一块并没有被写满。

根据两个BLOCK结构的分析,基本上确认了文章开始的分析,也就是Oracle一个块无法写下全部记录的时候,会通过行链接实现,而实现的方式就是在块头处通过ROWID的方式之处下一个存放记录的块的位置。

虽然这个问题解决了,但是还存在一个新的问题。前面提到了,Oracle并不是以列作为划分记录的单位,一列的数据可能分布到多个BLOCK上,比如这个例子。那么Oracle是如何确定当前BLOCK存放最后一列记录是否完整呢。

每个BLOCK中,数据存放都是从COL0开始排序递增的,因此这个COL的编号是块内的编号,而不是记录内的列的编号,显然无法通过这个COL位置来确定列的信息。

虽然这个例子本身没有疑问,因为表本身包含3个列,而每个BLOCK都有两个列,显然是第二个列在第一个BLOCK中没有完全存储,部分内容写到了第二个BLOCK中。但是这种分析并不适用所有的情况,假设当前表就是4个字段,那么根据DUMP的结果就没有办法判断是COL2被分散到了两个BLOCK,且COL4为空;还是COL2的长度就是18,而COL3的长度是3982COL4长度是4000

因此,Oracle肯定通过其他的手段记录了这个信息。

yangtingkun 发表于:2008.06.28 23:51 ::分类: ( ORACLE ) ::阅读:(356次) :: 评论 (0)

发表评论
标题

在此添加评论
表情符号: smile laughing tongue angry crying sad wassat wink

称呼

邮箱地址(可选)

个人主页(可选)

 authimage


切换风格
新闻聚合
博客日历
文章归档...
最新发表...
最新评论...
最多阅读文章...
最多评论文章...
博客统计...
Blog信息
网站链接...