首页 > 数据库 > Oracle > Row Migration和row chained
数据块头:主要包含有数据块地址的一些基本信息和段的类型,以及表和包含有数据的实际行的地址。
自由空间:是指可以为以后的更新和插入操作分配的空间,大小由PCTFREE和PCTUSED两个参数影响。
实际数据:是指在行内存储的实际数据。
当创建或者更改任何表和索引的时候,Oracle在空间控制方面使用两个存储参数:
PCTFREE:为一个块保留的空间百分比,表示数据块在什么情况下可以被insert,默认是10,表示当数据块的可用空间低于10%后,就不可以被insert了,只能被用于update。当使用一个block时,在达到pctfree之前,该block是一直是空闲状态,这个时候处在上升期。 一旦一个INSERT操作使得数据块的90%被使用,这个数据块就从空闲列表(free list)中移出。
PCTUSED:是指当块里的数据低于多少百分比时,又可以重新被insert,一般默认是40,即40%,即:当数据低于40%时,又可以写入新的数据,这个时候处在下降期。当记录从数据表中删除时,数据库的数据块就有空间接受新的记录,但只有当填充的空间降到PCTUSED值以下时,该数据块才被连接到空闲列表中,才可以往其中插入数据。
当插入一条记录的时候,Oracle会在free list中先去寻找一个自由的块,并且将数据插入到这个自由块中。而在free list中存在的自由的块是由pctfree值决定的。初始的空块都是在free list中的,直到块中的自由空间达到pctfree的值,此块就会从free list中移走,而当此块中的使用空间低于pctused的时候,此块又被重新放到free list中。
如图:
假设PCTFREE=20 PCTUSED=40 新创建的表中,数据块被不断插入数据,当数据达到整个数据块的80%,此时该数据块被从free list移出,不再提供空间进行新的数据插入,预留出20%的空间提供给对该数据块的update操作,而当随着删除该数据块上的数据,只有当该数据块数据比例达到40%以下,才可以插入新的数据。
行迁移(Row Migration)
当对表中数据进行update操作时,如果被更新的数据占用的空间要比原来的数据占用空间大到pctfree预留出的空间也不够用,此时 Oracle会将整行的数据迁移到一个新的数据块上,而将该行原先的空间只放一个指针,指向该行的新的位置,并且该行原先空间的剩余空间不再被数据库使用,这些剩余的空间我们将其称之为空洞,这就是产生表碎片的主要原因,表碎片基本上也是不可避免的,但是我们可以将其降到一个我们可以接受的程度。注意,即使发生了行迁移,发生了行迁移的行的rowid 还是不会变化,这也是行迁移会引起数据库I/O性能降低的原因。
行链接(row chained )
当使用insert 插入一条新的记录时,一个block空间不够容纳一条记录,这种情况下,Oracle将使用链接一个或者多个在这个段中保留的block存储这一行记录,行链接比较容易发生在比较大的行上,例如行上有LONG、LONG RAW、LOB等数据类型的字段,这种时候行链接是不可避免的会产生的。
例子
创建一个测试表,因为db_block_size是8K,为了达到效果,测试表中的每行的字段长度要超过8196。如test表中有五个char字段,单行数据位10000kb超出8k,插入单行数据就将处罚行链接
SQL> create table test(
2 id char(2000),
3 value1 char(2000),
4 value2 char(2000),
5 value3 char(2000),
6 value4 char(2000))
7 pctfree 20
8 pctused 50
9 tablespace users
10 /
Table created.
先插入一个字段的数据,查看一下数据块,dump出该数据块信息
SQL> insert into test(id) values('aa');
1 row created.
SQL> commit;
Commit complete.
SQL> select
2 dbms_rowid.rowid_relative_fno(rowid)rel_fno,
3 dbms_rowid.rowid_block_number(rowid)blockno,
4 dbms_rowid.rowid_row_number(rowid)rowno
5 from test
6 /
REL_FNO BLOCKNO ROWNO
---------- ---------- ----------
4 1092 0
SQL> alter system dump datafile 4 block 1092;
System altered.
SQL> oradebug setmypid;
Statement processed.
SQL> oradebug tracefile_name;
/u01/app/oracle/admin/orcl_dup/udump/orcl_dup_ora_4302.trc
data_block_dump,data header at 0xd96d664
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x0d96d664
bdba: 0x01000444
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x17c2
avsp=0x17ae
tosp=0x17ae
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x17c2
block_row_dump:
tab 0, row 0, @0x17c2
tl: 2006 fb: --H-FL-- lb: 0x1 cc: 1
col 0: [2000]
61 61 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
其中
fb:H是指行记录的头,L是指行记录的最后一列,F是指行记录的第一列。
cc:列的数量。
nrid:对于行链接或者行迁移来说的下一个row id的值。
更新一下该行数据
SQL> update test set id='id',value1='value1',value2='value2',value3='value3',value4='value4' where id='aa';
1 row updated.
SQL> commit;
Commit complete.
再次dump出该块
data_block_dump,data header at 0xdbe2664
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x0dbe2664
bdba: 0x01000444
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0xfb1
avsp=0x1773
tosp=0x1773
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0xfb1
block_row_dump:
tab 0, row 0, @0xfb1
tl: 2065 fb: --H-F--N lb: 0x2 cc: 2
nrid: 0x01000445.0
col 0: [2000]
69 64 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
注意到此处的fb上L转换成N N代表接下来的数据记录在另外的数据块上
根据nrid查出具体的数据块
其中cc:2第二个列记录的是行链接的地址
SQL> select
2 to_number('1000445','xxxxxxxxxx') rowid_tonu,
3 dbms_utility.data_block_address_file(to_number('1000445','xxxxxxxxxx')) file#,
4 dbms_utility.data_block_address_block(to_number('1000445','xxxxxxxxxx')) blcok#
5 from dual;
Dump该数据块会发现,fb上p表示第一列的值来自上个数据块的连接。cc的值是4也就是说1092数据块中记录的链接rowid指定的该块记录了剩下的四列的数据
ROWID_TONU FILE# BLCOK#
---------- ---------- ----------
16778309 4 1093
data_block_dump,data header at 0xdc1367c
===============
tsiz: 0x1f80
hsiz: 0x14
pbl: 0x0dc1367c
bdba: 0x01000445
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x65
avsp=0x51
tosp=0x51
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x65
block_row_dump:
tab 0, row 0, @0x65
tl: 7963 fb: -----LP- lb: 0x1 cc: 4
为了方便检查行迁移和行链接信息,oracle提供了一个脚本:utlchain.sql,该脚本在$ORACLE_HOME/rdbms/admin中,执行该脚本生成chained_rows表,通过analyze table tname list chained rows into chained_rows可以逐个的分析表。
SQL> @?/rdbms/admin/utlchain.sql
Table created.
SQL> analyze table test list chained rows into chained_rows;
Table analyzed.
查询表的行迁移或行链接的次数
SQL> select table_name,count(*) from chained_rows group by table_name;
TABLE_NAME COUNT(*)
--------------- ----------
TEST 1
通过v$sysstat视图查找行迁移和行链接的总数
SQL> select name,value from v$sysstat where name='table fetch continued row';
NAME VALUE
------------------------------ ----------
table fetch continued row 5044
1、借用chained_rows表清除
/*如果不是第一次创建chained_rows为免以前的分析数据影响操作,记得对改变truncate该表*,上面演示用的test表因为每行的数据都超过了db_block_size的限制,所以每行数据都会肯定有行迁移,不适合做该测试/
1 create table test2 (
2 id char(2000),
3 test2 char(2000))
4 pctfree 30
5 pctused 40
6* tablespace users
SQL> /
Table created.
SQL> @?/rdbms/admin/utlchain.sql
Table created.
SQL> analyze table test list chained rows into chained_rows;
Table analyzed.
SQL> select table_name,count(*) from chained_rows group by table_name;
TABLE_NAME COUNT(*)
--------------- ----------
TEST 19
SQL> create table test_emp
2 as
3 select * from test
4 where rowid in(
5 select head_rowid from chained_rows where table_name='TEST')
6 /
Table created.
SQL> delete from test
2 where rowid in(
3 select head_rowid from chained_rows where table_name='TEST')
4 /
8 rows deleted.
SQL> commit;
Commit complete.
SQL> insert into test select * from test_emp;
8 rows created.
SQL> commit;
Commit complete.
SQL> analyze table test list chained rows into chained_rows;
Table analyzed.
SQL> select table_name,count(*) from chained_rows group by table_name;
TABLE_NAME COUNT(*)
--------------- ----------
TEST 0
2、通过move移动表消除行迁移
SQL> alter table test2 move;
Table altered.
或者
SQL> alter table test2 move tablespace tts_test;
Table altered.
3、使用exp/imp或者expd/impd工具导出导入表
1、适当的增大db_cache_size的值,即增大block的容量,这样单个block容纳更多的数据,可以有效减少行连接和行迁移数量,但是该参数最好在数据库安装时设置,并且要是自己系统的block的整数倍。
2、设置合适的pctfree和pctused能有效的减少行迁移和行链接的次数,比如如果对表的update操作较为频繁,而且每次变更的数据所占空间相差较大,可以考虑将pctfree设置的大点,预留出更多的空间。pctused的设置更多的是考虑对块的利用率上,如果delete和insert较为频繁,并且系统所有空间有限,为了避免浪费空间,可以将pctused的值设置大点,反之,如果考虑到insert的效率,可以将该值设置小一些。给出的建议公式如下:
PCTFREE = 100 × upd / (average row length)
PCTUSED = 100 – PCTFREE – 100 × rows × (average row length) / blocksize
upd : the average amount added by updates, in bytes。This is determined by subtracting the average row length of intercurrent average row length
average row length:在运行了analyize命令之后,这个值可以从dba_tables中的avg_row_len列中获得。
rows : the number of rows to be deleted before free list maintenance occurs。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29320885/viewspace-1241573/ ,如需转载,请注明出处,否则将追究法律责任。