有两种情况会导致表中某行数据过大,一个数据块无法容纳。
第一种情况:当一行数据被插入时一个数据块就无法容纳,在这种情况下oracle将这行数据存储在段内的一个数据块链中。在插入数据量大的行时常会发生行链接(row chaining)。例如一个包含数据类型为long或long raw列的数据行,此时行链接不可避免。
第二种情况:原本存储在一个数据块内的数据行,因为更新操作导致长度增长,而所在数据块的可用空间也不能容纳增长后的数据行。在这种情况下,oracle将此行数据迁移到新的数据块中,oracle在被迁移数据行原本所在位置保存一个指向新数据块的指针。被迁移数据行的rowid保持不变。当数据行发生链接或迁移时,对其访问将会造成I/O性能降低,因为oracle为获取这些数据行的数据时,必须访问更多的数据块。
可以看到行迁移发生在update,有一部分数据放在当前块,有一部分数据放在链接的块中。行链接在当前块只放一个地址,内容全部在链接的块中,且可能有多个链接块。
模拟行迁移(行迁移实验有误,请看最后一部分实验进行重做):
SQL> create table testrow(id char(100),id2 varchar2(4000),id3 varchar2(4000)) pctfree 98 pctused 2;
Table created
SQL> insert into testrow values('1','2','3');
1 row inserted
SQL> commit;
Commit complete
SQL> @?/rdbms/admin/utlchain.sql
Table created
SQL> ANALYZE TABLE testrow LIST CHAINED ROWS;
Table analyzed
SQL> select OWNER_NAME,TABLE_NAME,HEAD_ROWID,ANALYZE_TIMESTAMP from chained_rows;
OWNER_NAME TABLE_NAME HEAD_ROWID ANALYZE_TIMESTAMP
------------------------------ ------------------------------ ------------------ -----------------
SQL> update testrow set id2=RPAD('Z',4000,'Z'),id3=RPAD('H',4000,'H');
1 row updated
SQL> commit;
Commit complete
SQL> ANALYZE TABLE testrow LIST CHAINED ROWS;
Table analyzed
SQL> select OWNER_NAME,TABLE_NAME,HEAD_ROWID,ANALYZE_TIMESTAMP from chained_rows;
OWNER_NAME TABLE_NAME HEAD_ROWID ANALYZE_TIMESTAMP
------------------------------ ------------------------------ ------------------ -----------------
EAM_ARCHIVE TESTROW AAAbzOAAFAADtqEAAA 2012-10-8 17:37:3
SQL>
SQL> select rowid,dbms_rowid.rowid_object(rowid) object_id,--51366(AAAMim)AAFAAAAAMAAC 数据对象号
2 dbms_rowid.rowid_relative_fno(rowid) file_id, --5 AAAMim(AAF)AAAAAMAAC 相对文件号
3 dbms_rowid.rowid_block_number(rowid) block_id, --12 AAAMimAAF(AAAAAM)AAC 在第几个块
4 dbms_rowid.rowid_row_number(rowid) num --2 AAAMimAAFAAAAAM(AAC)在block中的行数
5 from testrow;
ROWID OBJECT_ID FILE_ID BLOCK_ID NUM
------------------ ---------- ---------- ---------- ----------
AAAbzOAAFAADtqEAAA 113870 5 973444 0
SQL> alter system dump datafile 5 block 973444;
System altered
SQL>
SQL> select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('014eda85', --nrid: 0x014eda85.0
2 'xxxxxxxxxx')) file#,
3 dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('014eda85',
4 'xxxxxxxxxx')) block#
5 from dual;
FILE# BLOCK#
---------- ----------
5 973445
SQL> alter system dump datafile 5 block 973445;
System altered
Block header dump: 0x014eda84
Object id on Block? Y
seg/obj: 0x1bcce csc: 0x959.d8f4f38a itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x14eda81 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0013.029.0000417a 0x03c4bf7c.0178.2d C--- 0 scn 0x0959.d8f4f338
0x02 0x000d.00b.000041df 0x03c04f0f.01a4.0b --U- 1 fsc 0x0000.d8f4f38f
data_block_dump,data header at 0xa43ac64
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x0a43ac64
bdba: 0x014eda84
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1e93
avsp=0x1eeb
tosp=0x1eeb
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1e93
block_row_dump:
tab 0, row 0, @0x1e93
tl: 153 fb: --H-F--N lb: 0x2 cc: 2
nrid: 0x014eda85.0 --迁移的块的地址
col 0: [100]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 1: [42]--上一部分
5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a
5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a
end_of_block_dump
Block header dump: 0x014eda85
Object id on Block? Y
seg/obj: 0x1bcce csc: 0x959.d8f4f38a itc: 3 flg: E typ: 1 - DATA
brn: 0 bdba: 0x14eda81 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000d.00b.000041df 0x03c04f0f.01a4.0a --U- 1 fsc 0x0000.d8f4f38f
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 0xa43ac7c
===============
tsiz: 0x1f80
hsiz: 0x14
pbl: 0x0a43ac7c
bdba: 0x014eda85
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x61
avsp=0x4d
tosp=0x4d
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x61
block_row_dump:
tab 0, row 0, @0x61
tl: 7967 fb: -----LP- lb: 0x1 cc: 2
col 0: [3958]--下一部分
5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a
5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a
................................省略很多...................................
col 1: [4000]
48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48
48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48
................................省略很多...................................
end_of_block_dump
注:
--翻译20 为Z
select to_number('5a','xxxx')from dual;
select chr(90)from dual;
--翻译20 为
select to_number('20','xxxx')from dual;
select chr(32)from dual;
--翻译48 为H
select to_number('48','xxxx')from dual;
select chr(72)from dual;
行链接实验:
SQL> create table t
2 (a varchar2(4000),
3 b varchar2(4000),
4 c varchar2(4000));
SQL> insert into t values
2 (lpad('1', 4000, '1'),
3 lpad('2', 4000, '2'),
4 lpad('3', 4000, '3'));
SQL> commit;
SQL> select rowid,dbms_rowid.rowid_object(rowid) object_id,--51366(AAAMim)AAFAAAAAMAAC 数据对象号
2 dbms_rowid.rowid_relative_fno(rowid) file_id, --5 AAAMim(AAF)AAAAAMAAC 相对文件号
3 dbms_rowid.rowid_block_number(rowid) block_id, --12 AAAMimAAF(AAAAAM)AAC 在第几个块
4 dbms_rowid.rowid_row_number(rowid) num --2 AAAMimAAFAAAAAM(AAC)在block中的行数
5 from t;
ROWID OBJECT_ID FILE_ID BLOCK_ID NUM
------------------ ---------- ---------- ---------- ----------
AAAXUTAAGAAAEe9AAA 95507 6 18365 0
SQL> alter system dump datafile 6 block 18365;
System altered
data_block_dump,data header at 0xa768464
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x0a768464
bdba: 0x018047bd
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x40
avsp=0xfd8
tosp=0xfd8
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x40
block_row_dump:
tab 0, row 0, @0x40
tl: 4012 fb: --H-F--- lb: 0x1 cc: 1
nrid: 0x018047bc.0--链接的块的地址
col 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
................................省略.....................................
end_of_block_dump
SQL>select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('018047bc',
'xxxxxxxxxx')) file#,
dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('018047bc',
'xxxxxxxxxx')) block#
from dual;
SQL> alter system dump datafile 6 block 18364;
System altered
链接块1
data_block_dump,data header at 0xa76847c
===============
tsiz: 0x1f80
hsiz: 0x14
pbl: 0x0a76847c
bdba: 0x018047bc
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x2e
avsp=0xfc0
tosp=0xfc0
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x2e
block_row_dump:
tab 0, row 0, @0x2e
tl: 4012 fb: -------- lb: 0x2 cc: 1
nrid: 0x018047c0.0--链接的块的地址
col 0: [4000]
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
................................省略.....................................
end_of_block_dump
SQL>select dbms_utility.DATA_BLOCK_ADDRESS_FILE(to_number('018047c0',
'xxxxxxxxxx')) file#,
dbms_utility.DATA_BLOCK_ADDRESS_BLOCK(to_number('018047c0',
'xxxxxxxxxx')) block#
from dual;
SQL> alter system dump datafile 6 block 18368;
System altered
链接块2
data_block_dump,data header at 0xa76847c
===============
tsiz: 0x1f80
hsiz: 0x14
pbl: 0x0a76847c
bdba: 0x018047c0
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0xfda
avsp=0xfc6
tosp=0xfc6
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0xfda
block_row_dump:
tab 0, row 0, @0xfda
tl: 4006 fb: -----L-- lb: 0x3 cc: 1
col 0: [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
................................省略.....................................
end_of_block_dump
重做行迁移实验:要想构造出行迁移,我们要有点特殊的手段,需要尽量让数据块满,只要把pctfree设置小一点就可以了。插入一些数据后,找到一个块满的,然后做修改,由于块中已没有空间支持行链接,则发生行迁移,是整行发生迁移。
-- 注意pctfree为2
SQL> create table t
(a varchar2(4000),
b varchar2(4000),
c varchar2(4000)) pctfree 2;
SQL> begin
for i in 1.. 1000
loop
insert into t values(i,i,i);
end loop;
commit;
end;
/
PL/SQL 过程已成功完成。
-- 找到一个数据记录较多的数据块
SQL> select count(1),block_id from
(select rowid,a,
dbms_rowid.rowid_object(rowid) object_id,
dbms_rowid.rowid_relative_fno(rowid) file_id,
dbms_rowid.rowid_block_number(rowid) block_id,
dbms_rowid.rowid_row_number(rowid) num
from t )group by block_id;
COUNT(1) BLOCK_ID
---------- ----------
465 1471
484 1470
51 1472
-- 找到它数据范围
SQL> select max(a) max_a,min(a) min_a from(select rowid,a,
2 dbms_rowid.rowid_object(rowid) object_id,
3 dbms_rowid.rowid_relative_fno(rowid) file_id,
4 dbms_rowid.rowid_block_number(rowid) block_id,
5 dbms_rowid.rowid_row_number(rowid) num
6 from t) where block_id=1471;
MAX_A MIN_A
---------- ----------
949 485
SQL> update t set b=lpad('2', 4000, '2'),c=lpad('2', 4000, '2')
2 where to_number(a) >484 and to_number(a) <950;
已更新465行。
SQL> commit;
SQL> alter system dump datafile 4 block 1471;
系统已更改。
trace的文件:黄色部分就是行迁移
tl: 9 fb: --H----- lb: 0x2 cc: 0
nrid: 0x010005f0.0
tab 0, row 10, @0x1e89
tl: 9 fb: --H----- lb: 0x2 cc: 0
nrid: 0x010005f7.0
tab 0, row 11, @0x1e80
tl: 9 fb: --H----- lb: 0x2 cc: 0
nrid: 0x010005f1.0
tab 0, row 12, @0x1e77
tl: 9 fb: --H----- lb: 0x2 cc: 0
nrid: 0x010005f3.0
tab 0, row 13, @0x1e43
tl: 52 fb: --H-F--N lb: 0x2 cc: 2
nrid: 0x010005f4.0
col 0: [ 3] 34 39 38
col 1: [38]
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