Row Chaining and Row Migration

来源:互联网 发布:穆雅斓淘宝店 编辑:程序博客网 时间:2024/05/19 03:43

      本文主要介绍Oracle的行链接(Row Chaining)与行迁移(Row Migration)的产生原因及调整方法。如果数据库存在大量的行迁移及行链接,那么对数据库的性能影响是非常大的。关于数据在数据块中是如何存储的可以参考文章:http://blog.csdn.net/huang_tg/archive/2010/07/08/5722114.aspx及http://blog.csdn.net/huang_tg/archive/2010/07/09/5723596.aspx

      一.行链接(Row Chaining)

         行链接产生在第一次插入数据时如果一个block不能存放一行记录的情况下。在这种情况下Oracle将使用链接一个或者多个在这个段中保留的block存储这一行记录,行连接比较容易发生在比较大的行上,例如行上有LONG,LONG RAW,LOB等数据类型的字段,这种时候行连接是不可避免的会产生的。以为发生行连接的行会被分成多块,所以需要一个指针指向下一个块开始的地方。下图为行链接(Row Chaining)示意图:

row chaining

        1.行链接(Row Chaining)分析

          创建test表,使其单行数据的最大值大于当前数据库的db_block_size设置的值,向表中插入一行数据,然后通过show_space过程查看数据块中的数据存储详情,实验步骤如下:

SQL> show parameter db_block_size;

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------

db_block_size                        integer     4096

SQL> create table test

  2  (x number primary key,

  3   a char(2000),

  4   b char(2000),

  5   c varchar2(2000));

表已创建。

SQL> insert into test values (1,'test','test','test');

SQL> commit;

提交完成。

SQL> set serveroutput on

SQL> exec show_space('TEST');

Total Blocks............................3

Total Bytes.............................12288

Unused Blocks...........................0

Unused Bytes............................0

Last Used Ext FileId....................1

Last Used Ext BlockId...................61573

Last Used Block.........................3

PL/SQL 过程已成功完成。

SQL> alter system dump datafile 1 block 61574;

系统已更改。

SQL> alter system dump datafile 1 block 61575;

系统已更改。

block_row_dump:

tab 0, row 0, @0xf0a

tl: 150 fb: --H-F--N lb: 0x1 cc: 2 --Flag byte(fb)H=header F=first N=next

nrid:  0x0040f086.0

col  0: [ 2]  c1 02         --记录11列数据

col  1: [137]

 74 65 73 74 20…… 20 20   --记录12列部分数据

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 61575 maxblk 61575

 

block_row_dump:

tab 0, row 0, @0x63

tl: 3877 fb: -----LP- lb: 0x1  cc: 3

col  0: [1863]                --记录12列剩余数据起始物理位置

 20 20 20 …… 20 20 20 20    --记录12列剩余的数据

col  1: [2000]     

 74 65 73 74 20 …… 20 20   --记录13列的数据

col  2: [ 4]  74 65 73 74    --记录14列的数据

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574

      通过上面的实验我们可以看出之前Insert的数据占用了两个数据块,block 61574及61575分别存放了一行中的部分数据。也就是说我们需要查询这条数据的话,oracle至少需要扫描这俩个数据块。

      二.行迁移(Row Migration)

 

         当一行的记录初始插入时是可以存储在一个block中的,由于更新操作导致行增加了,而block的自由空间已经完全满了,这个时候就产生了行迁移。在这种情况下,oracle将会把整行数据迁移到一个新的block(假设一个block中可以存储下整行数据)oracle会保留被迁移的行的原始指针指向新的存放行数据的block,这就意味着被迁移行的ROW ID是不会改变的。下图为行迁移(Row Migration)的示意图:

row migration

        1.行迁移(Row Migration)分析

          使用于行链接类似的方法对行迁移(Row Migration)分析,一下是分析过程:

SQL> create table test

  2  (x number primary key,

  3   a char(1000),

  4   b varchar2(1000));

表已创建。

SQL> insert into test values (1,'test','test');

SQL> insert into test values (2,'test','test');

SQL> insert into test values (3,'test','test');

SQL> insert into test values (4,'test','test');

SQL> commit;

提交完成。

SQL> exec show_space('TEST');

Total Blocks............................3

Total Bytes.............................12288

Unused Blocks...........................0

Unused Bytes............................0

Last Used Ext FileId....................1

Last Used Ext BlockId...................61573

Last Used Block.........................3

PL/SQL 过程已成功完成。

SQL> alter system dump datafile 1 block 61574;

系统已更改。

SQL> alter system dump datafile 1 block 61575;

系统已更改。

迁移前:

block_row_dump:

tab 0, row 1, @0x7b4    --更改前的记录2物理地址

tl: 1014 fb: --H-FL-- lb: 0x1 cc: 3  flag byte(fb) H=header F=first L=last

col  0: [ 2]  c1 03     --更新前记录21列的数据

col  1: [1000]          --更新前记录22列的数据

 74 65 73 74 20……20

col  2: [ 4]  74 65 73 74

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574

迁移后

SQL> update test set b='aaa……aaa' where x=2;

SQL> commit;

提交完成。

SQL> alter system dump datafile 1 block 61574;

系统已更改。

SQL> alter system dump datafile 1 block 61575;

系统已更改。

block_row_dump:

tab 0, row 1, @0x1c8              --block 61574

tl: 9 fb: --H----- lb: 0x2  cc: 0 --fb:H=header 只有header信息

nrid:  0x0040f087.1               --指针,指向记录2迁移后的地址

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 61574 maxblk 61574

block_row_dump:

tab 0, row 1, @0x3c4    --记录2block 61575的开始地址

tl: 1998 fb: ----FL-- lb: 0x2  cc: 3  --fb:F=first,L=last cc:3 有三列

hrid: 0x0040f086.1      

col  0: [ 2]  c1 03     --记录21列数据,2个字节,数据为c1 03

col  1: [1000]          --记录22列信息  数据有1000个字节

 74 65 73 74 20……20   --记录22列的数据 省略了n20

col  2: [980]           --记录23列的信息数据有980个字节

 61 61……61 61         --记录23列的数据 省略了n61

end_of_block_dump

End dump data blocks tsn: 0 file#: 1 minblk 61575 maxblk 61575

      通过上述分析我们能很清楚的看到行迁移不仅会造成存储空间的浪费,还会造成查询的多读入数据块,如果数据库中存在大量的行迁移的话对性能的影响是非常大的。

      三.行迁移(Row Chaining)与行链接(Row Migration)的清除

         行链接主要是由于数据库的db_block_size不够大,对于一些大的字段没法在一个block中存储下而产生的。对于行链接,除了增大db_block_size之外没有别的任何办法避免,但是因为数据库建立后db_block_size是不可以改变的(9i以前),对于oracle 9i数据库意义对不同的表空间指定不同的db_block_size,因此行链接的产生几乎是不可避免的,也没有太多可以调整的地方。行迁移主要是由于更新表的时候,由于block上的PCTFREE参数设置过小所置,要实现控制行迁移的增长,就必须设置一个合理的PCTFREE参数,否则即使清除了当前的行迁移后马上又会产生新的行迁移。如果PCTFREE参数设置过大则会导致block的利用率低,大量的空间浪费。如何设置一个合理的PCTFREE值有两种方法可参考:

         1.定量设定法:

           就是利用公式来设定PCTFREE的大小,选使用ANALYZE TABLE table_name ESTIMATE STATISTICS命令来分析要修改的PCTFREE的表,然后查看user_table中的AVG_ROW_LEN列值,得到第一个平均长度AVG_ROW_LEN1,然后大量的操作以后,再次得到第二个平均长度AVG_ROW_LEN2,然后用公式:100*(AVG_ROW_LEN2-AVG_ROW_LEN1)/(AVG_ROW_LEN2-AVG_ROW_LEN1+AVG_ROW_LEN) 的出的结果就是定量计算出来的一个合适的PCTFREE数值。这种方法因为是定量计算出来的,不一定会准确,而且因为要分析表,所以对于使用RBO执行计划的系统不是很适用,例如,AVG_ROW_LEN1=60AVG_ROW_LEN2=70,则平均修改量为10PCTFREE应该调整为100*10/10+60=16.7%。

         2.差分微调法:

           先查询到当前的PCTFREE值,然后监控和调整PCTFREE参数,每次增加一点PCTFREE的大小,每次增加的比例不要超过5个百分点,然后使用ANALYZE TABLE table_name LIST CHAINED ROWS INTO chained_rows命令分析每次所有的行迁移和行连接的增长情况,对于不同的表采取不同的增长比例,对于行迁移增长的比较快的表的PCTFREE值就增加的多点,对于增加的慢的表的PCTFREE值增加少点,知道表的行迁移基本保持不增长为止。但是PCTFREE不应该增加的过大,一般在40%以下就可以了,否则就会造成空间的很大浪费和增加数据库访问I/O。