ITPub博客

首页 > 数据库 > Oracle > Row Migration和row chained

Row Migration和row chained

原创 Oracle 作者:静以致远√团团 时间:2014-07-29 19:20:58 0 删除 编辑

行迁移和行连接

OracleBlock的组成

数据块头:主要包含有数据块地址的一些基本信息和段的类型,以及表和包含有数据的实际行的地址。
自由空间:是指可以为以后的更新和插入操作分配的空间,大小由PCTFREEPCTUSED两个参数影响。 
实际数据:是指在行内存储的实际数据。

当创建或者更改任何表和索引的时候,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存储这一行记录,行链接比较容易发生在比较大的行上,例如行上有LONGLONG RAWLOB等数据类型的字段,这种时候行链接是不可避免的会产生的。

 

例子

创建一个测试表,因为db_block_size8K,为了达到效果,测试表中的每行的字段长度要超过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

 

其中

fbH是指行记录的头,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

注意到此处的fbL转换成N  N代表接下来的数据记录在另外的数据块上

根据nrid查出具体的数据块

其中cc2第二个列记录的是行链接的地址

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该数据块会发现,fbp表示第一列的值来自上个数据块的连接。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、设置合适的pctfreepctused能有效的减少行迁移和行链接的次数,比如如果对表的update操作较为频繁,而且每次变更的数据所占空间相差较大,可以考虑将pctfree设置的大点,预留出更多的空间。pctused的设置更多的是考虑对块的利用率上,如果deleteinsert较为频繁,并且系统所有空间有限,为了避免浪费空间,可以将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博客 ” ,链接:http://blog.itpub.net/29320885/viewspace-1241573/,如需转载,请注明出处,否则将追究法律责任。

上一篇: Oracle 之HWM
下一篇: pl/sql 练习
请登录后发表评论 登录
全部评论
每个人都有梦想,去实现吧!

注册时间:2013-11-14

  • 博文量
    164
  • 访问量
    2103462