[20140213]再论行迁移.txt

[20140213]再论行迁移.txt

昨天看jonathanlewis的blog,链接如下:
https://jonathanlewis.wordpress.com/2014/02/10/row-migration/

Oracle behaves quite intelligently with migrated rows. First, the migrated row has a pointer back to the original
location and if the row has to migrate a second time the first place that Oracle checks for space is the original block,
so the row might "de-migrate" itself; however, even if it can't migrate back to the original block, it will still
revisit the original block to change the pointer in that block to refer to the block it has moved on to – so the row
is never more than one step away from its original location. As a quick demonstration, here's some code to generate and
manipulate some data:

--我按照自己方式演示作者的例子。中间穿插我的说明。
1.测试环境:
SCOTT@test> @ver
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

--表空间使用assm管理。SEGMENT SPACE MANAGEMENT AUTO。

create table t1 (id number(6,0),v1 varchar2(1200)) pctfree 0 ;

prompt    ==========================================
prompt    The following code fits 74 rows to a block
prompt    ==========================================

insert into t1 select rownum - 1, rpad('x',100) from all_objects where rownum commit;
-------------------------------------------------------------------
SCOTT@test> select rowid,t1.id from t1;
ROWID                      ID
------------------ ----------
AABFP/AAEAAAAIOAAA          0
AABFP/AAEAAAAIOAAB          1
...
AABFP/AAEAAAAIOABJ         73
AABFP/AAEAAAAIPAAA         74
75 rows selected.

SCOTT@test> @lookup_rowid AABFP+AAEAAAAIOAAA
    OBJECT       FILE      BLOCK        ROW DBA                  TEXT
---------- ---------- ---------- ---------- -------------------- ----------------------------------------
    283647          4        526          0 4,526                alter system dump datafile 4 block 526 ;

SCOTT@test> @lookup_rowid AABFP/AAEAAAAIPAAA
    OBJECT       FILE      BLOCK        ROW DBA                  TEXT
---------- ---------- ---------- ---------- -------------------- ----------------------------------------
    283647          4        527          0 4,527                alter system dump datafile 4 block 527 ;

-- ID= 74 在另外1块,意味着dba=4,526的块是已经写满。在使用bbed观察。
BBED> set dba 4,526
        DBA             0x0100020e (16777742 4,526)

BBED> p *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0]                              @271      0x2c

BBED> x /rxncc
rowdata[0]                                  @271
----------
flag@271:  0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@272:  0x01
cols@273:     2
col    0[1] @274:  0x80
col  1[100] @276:  0x78  0x20  0x20  0x20  0x20 .. --太长截断

--可以发现id=0的记录没有发生行迁移。*kdbr[0]=271.

------------------------------------------------------------------------
prompt    ======================================
prompt    Make the first row migrate and dump it
prompt    ======================================

update t1 set v1 = rpad('x',400) where id = 0;
commit;

alter system flush buffer_cache;
-- execute dump_seg('t1',2)
-----------------------------------------------------------------------
--修改 id=0,增加长度400,这样该块放不下,会出现行迁移情况。我不使用dump,
--代替使用bbed观察.

BBED> x /rxncc
rowdata[0]                                  @271
----------
flag@271:  0x20 (KDRHFH)
lock@272:  0x02
cols@273:     0
nrid@274:0x0100020f.1

--可以发现发生行迁移,cols=0,意味2个字段都移动到dba=0x0100020f.1

SCOTT@test> @dfb 0100020f
    RFILE#     BLOCK#
---------- ----------
         4        527

TEXT
----------------------------------------
alter system dump datafile 4 block 527 ;

--也就是移动到了file#=5,block#=527的行号=1.

BBED> set dba 4,527
        DBA             0x0100020f (16777743 4,527)

BBED> p *kdbr[1]
rowdata[0]
----------
ub1 rowdata[0]                              @7667     0x0c

BBED> x /rxncc
rowdata[0]                                  @7667
----------
flag@7667: 0x0c (KDRHFL, KDRHFF)
lock@7668: 0x02
cols@7669:    2
hrid@7670:0x0100020e.0

col    0[1] @7676:  0x80
col  1[400] @7678:  0x78  0x20  0x20  0x20  0x20  --太长截断
-- 可以发现hrid = 0x0100020e.0,就是指向原来的位置。
SCOTT@test> @dfb 0100020e
    RFILE#     BLOCK#
---------- ----------
         4        526

TEXT
----------------------------------------
alter system dump datafile 4 block 526 ;

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

prompt    ===========================================================
prompt    Fill the block the long row is now in, force it to migrate,
prompt    then dump it again.
prompt    ===========================================================

insert into t1 select rownum + 75, rpad('x',100) from all_objects where rownum commit;

update t1 set v1 = rpad('x',800) where id = 0;
commit;

alter system flush buffer_cache;
--execute dump_seg('t1',3)

-----------------------------------------------------------------------------
--在插入75条记录,是file#=4,block#=527填满。在修改id=0的记录长度增加到800,
--这样file#=4,block#=527已经无法放下这个长的记录,再次出现行迁移的情况。

BBED> set dba 4,526
        DBA             0x0100020e (16777742 4,526)

BBED> p *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0]                              @271      0x20

BBED> x /rxncc
rowdata[0]                                  @271
----------
flag@271:  0x20 (KDRHFH)
lock@272:  0x01
cols@273:     0
nrid@274:0x0100020b.7

--这次nrid@274:0x0100020b.7,行迁移到别的地方。0x0100020b.7
SCOTT@test> @dfb 0100020b
    RFILE#     BLOCK#
---------- ----------
         4        523

TEXT
----------------------------------------
alter system dump datafile 4 block 523 ;

--迁移到file#=4,block#=523的第7行。
BBED> set dba 4,523
        DBA             0x0100020b (16777739 4,523)

BBED> p *kdbr[7]
rowdata[0]
----------
ub1 rowdata[0]                              @6618     0x0c

BBED> x /rxncc
rowdata[0]                                  @6618
----------
flag@6618: 0x0c (KDRHFL, KDRHFF)
lock@6619: 0x02
cols@6620:    2
hrid@6621:0x0100020e.0

col    0[1] @6627:  0x80
col  1[800] @6629:  0x78  0x20  0x20  0x20  0x20  --太长截断
-- 可以发现hrid = 0x0100020e.0,就是指向原来的位置。而原来的行迁移,变成如下:

BBED> set dba 4,527
        DBA             0x0100020f (16777743 4,527)

BBED> p *kdbr[1]
rowdata[7319]
-------------
ub1 rowdata[7319]                           @7667     0x1c

BBED> x /rxncc
rowdata[7319]                               @7667
-------------
flag@7667: 0x1c (KDRHFL, KDRHFF, KDRHFD)
lock@7668: 0x01
cols@7669:    0

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

prompt    ========================================================
prompt    Fill the block the long row is now in and shrink the row
prompt    to see if it returns to its original block. (It won't.)
prompt    ========================================================

insert into t1 select rownum + 150, rpad('x',100) from all_objects where rownum commit;

update t1 set v1 = rpad('x',50) where id = 0;
commit;

alter system flush buffer_cache;
-- execute dump_seg('t1',3)
------------------------------------------------------------------------------
--再次插入75条记录填满file#=4,block#=523块。修改id=0,这回是长度变小为50.

BBED> set dba 4,526
        DBA             0x0100020e (16777742 4,526)

BBED> p *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0]                              @271      0x20

BBED> x /rxncc
rowdata[0]                                  @271
----------
flag@271:  0x20 (KDRHFH)
lock@272:  0x02
cols@273:     0
nrid@274:0x0100020b.7

--虽然记录长度缩小,实际上nrid@274:0x0100020b.7,依旧没有变化。主要file#=4,block#=526无法容下变小的记录。
------------------------------------------------------------------------------

prompt    ========================================================
prompt    Make a lot of space in the first block and force the row
prompt    to migrate again to see if it migrates back. (It does.)
prompt    ========================================================

delete from t1 where id between 1 and 20;
commit;

update t1 set v1 = rpad('x',1200) where id = 0;
commit;

alter system flush buffer_cache;
-- execute dump_seg('t1',3)

------------------------------------------------------------------------
--删除id=1 到 20的记录,这样空出file#=4,block#=526的空间,这样再修改id=0的记录,
--即使长度增加到1200,这样可以容下,看看行迁移的情况。
BBED> set dba 4,526
        DBA             0x0100020e (16777742 4,526)

BBED> p *kdbr[0]
rowdata[0]
----------
ub1 rowdata[0]                              @1309     0x2c

BBED> x /rxncc
rowdata[0]                                  @1309
----------
flag@1309: 0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@1310: 0x02
cols@1311:    2

col    0[1] @1312:  0x80
col 1[1200] @1314:  0x78  0x20  0x20  --太长截断。

--可以发现数据迁移回来了,这个纠正我原来的错误观点,发生了行迁移以后是回不来的。实际上是可以回来了。
--如果仔细看*kdbr[0]已经指向了@1309,而不是前面的@271.我们看看@271的情况

BBED> set offset 271
        OFFSET          271

BBED> x /rxncc
freespace[5]                                @271
------------
flag@271:  0x20 (KDRHFH)
lock@272:  0x02
cols@273:     0
nrid@274:0x0100020b.7

BBED> set dba 4,523
        DBA             0x0100020b (16777739 4,523)

BBED> p *kdbr[7]
rowdata[0]
----------
ub1 rowdata[0]                              @293      0x1c

BBED> x /rxncc
rowdata[0]                                  @293
----------
flag@293:  0x1c (KDRHFL, KDRHFF, KDRHFD)
lock@294:  0x02
cols@295:     0

BBED> set offset 6618
        OFFSET          6618

BBED> x /rxncc
rowdata[6325]                               @6618
-------------
flag@6618: 0x0c (KDRHFL, KDRHFF)
lock@6619: 0x00
cols@6620:    2
hrid@6621:0x0100020e.0

col    0[1] @6627:  0x80
col  1[800] @6629:  0x78  0x20  0x20  --截断。
------------------------------------------------------------------------

总结:
发生行迁移后,如果条件可以,行迁移是可以回来的。

使用的脚本:
--dfb.sql
select
dbms_utility.data_block_address_file(to_number('&1','xxxxxxxxxxxxxxxx')) rfile#,
dbms_utility.data_block_address_block(to_number('&&1','xxxxxxxxxxxxxxxx')) block#
from dual;

select 'alter system dump datafile '||dbms_utility.data_block_address_file(to_number('&1','xxxxxxxxxxxxxxxx'))||' block '||
dbms_utility.data_block_address_block(to_number('&&1','xxxxxxxxxxxxxxxx')) ||' ;' text
from dual;

--lookup_rowid.sql
set verify off
column dba format a20
column text format a40
SELECT DBMS_ROWID.ROWID_OBJECT ('&1') "OBJECT",
       DBMS_ROWID.ROWID_RELATIVE_FNO ('&1') "FILE",
       DBMS_ROWID.ROWID_BLOCK_NUMBER ('&1') "BLOCK",
       DBMS_ROWID.ROWID_ROW_NUMBER ('&1') "ROW",
          DBMS_ROWID.ROWID_RELATIVE_FNO ('&1')
       || ','
       || DBMS_ROWID.ROWID_BLOCK_NUMBER ('&1')
          "DBA",
          'alter system dump datafile '
       || DBMS_ROWID.ROWID_RELATIVE_FNO ('&1')
       || ' block '
       || DBMS_ROWID.ROWID_BLOCK_NUMBER ('&1')
       || ' ;'
          text
  FROM DUAL;

时间: 2024-09-20 15:05:18

[20140213]再论行迁移.txt的相关文章

[20121116]通过bbed观察行链接与行迁移.txt

[20121116]通过bbed观察行链接与行迁移.txt     如果应用中出现大量的行链接与行迁移,对应用的性能多少存在影响.一般情况下,行迁移主要是update后,行记录变大,导致原来的数据块无法容纳,在原来的块保留指针,其他信息放在其他块中.而行链接主要是行记录太大,1个数据块无法容纳,导致使用多块保存.我想通过bbed简单观察这种情况: 1.建立测试环境: SQL> select * from v$version where rownum BANNER ----------------

[20160803]另类行迁移.txt

[20160803]另类行迁移.txt --前几天做测试时,链接: http://blog.itpub.net/267265/viewspace-2122712/=>[20160729]行链接行迁移与ITL槽4.txt --发现一个块中的记录在字段长度变长后全部发生行迁移,感觉很奇怪,当时也没有仔细思考(开始以为至少有一些记录不会发生行迁移的 --情况),事后才想起来以前我做过类似的测试,参考链接: http://blog.itpub.net/267265/viewspace-1742243/=

[20160726]行链接行迁移与ITL槽.txt

[20160726]行链接行迁移与ITL槽.txt 当表中一行的数据不能在一个数据block中放入的时候,这个时候就会发生两种情况,一种是行链接(Row Chaining),另外一种就是行迁 移(Row Migration)了. 行链接产生在第一次插入数据的时候如果一个block不能存放一行记录的情况下.这种情况下,Oracle将使用链接一个或者多个在这个段 中保留的block存储这一行记录,行链接比较容易发生在比较大的行上,例如行上有LONG.LONG RAW.LOB等数据类型的字段,这种时候

[20130607]行迁移与ALTER TABLE MINIMIZE RECORDS_PER_BLOCK.txt

[20130607]行迁移与ALTER TABLE MINIMIZE RECORDS_PER_BLOCK.txt 前一阵子,在ITPUB上讨论避免行迁移的方法.想到ALTER TABLE MINIMIZE RECORDS_PER_BLOCK. 当数据行发生大量的行迁移(migrate)时,对其访问将会造成 I/O 性能降低,因为Oracle为获取这些数据行的数据时,必须访问更多的数据块(data block).而一般常规的解决方法就是增加PCTFREE的设置,预留更多的空间给行记录增长,但是又带

[20160728]]行链接行迁移与ITL槽3.txt

[20160728]]行链接行迁移与ITL槽3.txt --上午测试了行链接行迁移与ITL槽的关系,链接如下: [20160727]行链接行迁移与ITL槽2.txt => http://blog.itpub.net/267265/viewspace-2122663/ --如果仔细看前面的测试可以发现当出现行链接或者行迁移时,除了增加1个空itl槽像如下: 0x05   0x0000.000.00000000  0x00000000.0000.00  C---    0  scn 0x0000.0

[20160729]行链接行迁移与ITL槽4.txt

[20160729]行链接行迁移与ITL槽4.txt --做了几个测试,有点乱. http://blog.itpub.net/267265/viewspace-2122700/ http://blog.itpub.net/267265/viewspace-2122663/ http://blog.itpub.net/267265/viewspace-2122599/ --还是通过1个例子来模拟看看.做一个非常极端的测试: 1.环境: SCOTT@book> @ &r/ver1 PORT_ST

[20140209]行迁移和expdp导出.txt

[20140209]行迁移和expdp导出.txt 前一阵子与别人聊天,谈到一个系统升级expdp导出很慢,我比较熟悉这个系统,当他说出导出很慢的那张表的时候, 我随口讲不会这个表存在大量的行迁移吧.我记得以前学习oracle,听别人讲课,讲过一句话,如果你看这个系统 的用户模式下所有表的pctfree设置都是10的话,那么这个系统没有dba管理.按照这样看,中国大部分数据库系统 没有dba管理. 我自己以前对行迁移还是比较重视的,当然现在变懒了.我发现许多dba也不是太重视这个问题,慢慢我对这

Oracle数据库中行迁移/行链接学习(一)什么是行迁移/行链

在实际的工作中我们经常会碰到一些Oracle数据库性能较低的问题,当然,引起Oracle数据库性能较低的原因是多方面的,我们能够通过一些正确的设计和诊断来尽量的避免一些Oracle数据库性能不好,Row Migration (行迁移) & Row Chaining (行链接)就是其中我们可以尽量避免的引起Oracle数据库性能低下的潜在问题.通过合理的诊断行迁移/行链接,我们可以较大幅度上提高Oracle数据库的性能. 那究竟什么是行迁移/行链接呢,先让我们从Oracle的block开始谈起.

行链接 行迁移的消除

模拟行链接: 如何模仿行链接? 首先要了解三个语句: 第1句:运行$ORACLE_HOME/rdbms/admin/utlchain.sql 脚本,SQL> @D:\oracle\product\10.1.0\Db_1\RDBMS\ADMIN\utlchain.sql创建chained_rows表,用于存放发生行迁移         的行的rowid. 第2句:运行analyze table table_name list chained rows into chained_rows; 把产生行