[20160803]另類行遷移.txt

lfree發表於2016-08-03

[20160803]另類行遷移.txt

--前幾天做測試時,連結:
http://blog.itpub.net/267265/viewspace-2122712/=>[20160729]行連結行遷移與ITL槽4.txt

--發現一個塊中的記錄在欄位長度變長後全部發生行遷移,感覺很奇怪,當時也沒有仔細思考(開始以為至少有一些記錄不會發生行遷移的
--情況),事後才想起來以前我做過類似的測試,參考連結:
http://blog.itpub.net/267265/viewspace-1742243/=> [20150720]為什麼8K資料塊Hakan Factor=736

--為了加強記憶,重複測試:

1.環境:
SCOTT@book> @ &r/ver1
PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

create table t (id number,pad varchar2(200));
alter table t pctfree 0;
create unique index pk_t on t(id);
alter table t add constraint pk_t primary key (id) enable validate;

insert into t(id)   select rownum from dual connect by level<=734;
commit ;

SCOTT@book> select rowid,id from t where id =1 or id=734 or id=733;
ROWID                      ID
------------------ ----------
AAAXWyAAEAAAALrAAA          1
AAAXWyAAEAAAALrALc        733
AAAXWyAAEAAAALsAAA        734

SCOTT@book> @ &r/rowid AAAXWyAAEAAAALrAAA
    OBJECT       FILE      BLOCK        ROW DBA                  TEXT
---------- ---------- ---------- ---------- -------------------- ----------------------------------------
     95666          4        747          0 4,747                alter system dump datafile 4 block 747 ;
--//dba=4,747,一塊共733條記錄。

SCOTT@book> alter system checkpoint ;
System altered.

--按照前面的理論,一條記錄至少保留9個位元組.
BBED> set dba 4,747
        DBA             0x0240008e (37748878 9,142)

BBED> map
File: /mnt/ramdisk/book/users01.dbf (4)
Block: 747                                   Dba:0x010002eb
------------------------------------------------------------
KTB Data Block (Table/Cluster)
struct kcbh, 20 bytes                      @0
struct ktbbh, 72 bytes                     @20
struct kdbh, 14 bytes                      @100
struct kdbt[1], 4 bytes                    @114
sb2 kdbr[733]                              @118
ub1 freespace[1579]                        @1584
ub1 rowdata[5025]                          @3163
ub4 tailchk                                @8188

-- 計算如下:
8192-1584-4=6604 (扣除尾部tailchk4個位元組)
6604/733=9.00954979536152796725 (平均9個位元組,如果全部記錄發生行遷移,至少要儲存9個位元組)
6604-733*9=7 (餘數)

BBED> x /rnc *kdbr[0]
rowdata[3341]                         @6504
-------------
flag@6504: 0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@6505: 0x01
cols@6506:    1
col    0[2] @6507: 1

BBED> x /rnc *kdbr[1]
rowdata[3347]                         @6510
-------------
flag@6510: 0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@6511: 0x01
cols@6512:    1
col    0[2] @6513: 2

--//這裡1條記錄佔6個位元組,數字1佔2個位元組.還剩下9-6=3個位元組,其中一個作為長度指示器,這樣修改欄位pad字元長度增
--//加3,應該就會出現行遷移的情況.測試看看.

2.測試:
$ cat a.sql
update t set pad=lpad('x',3,'x') where id=&&1;
commit ;
quit;

spool b.sh
select 'sqlplus -s scott/book @a.sql '|| rownum from dual connect by level<=734;
spool off

--//編輯整理,執行b.sh.這樣將產生大量的行遷移.

SCOTT@book> alter system checkpoint ;
System altered.

SCOTT@book> alter system dump datafile 4 block 747 ;
System altered.

$ grep nrid /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_39869.trc | cut -d":" -f2 | cut -d"." -f1 | sort | uniq -c
    167   0x010002ec
    167   0x010002ed
    167   0x010002ee
    167   0x010002ef
     61   0x010002f9

167+167+167+167+61=729,僅僅4條沒有發生行遷移.

--轉儲其中1塊0x010002ec
SCOTT@book> @ &r/dfb16 0x010002ec
    RFILE#     BLOCK# TEXT
---------- ---------- ------------------------------------------------------------
         4        748 alter system dump datafile 4 block 748 ;

SCOTT@book> alter system checkpoint ;
System altered.

SCOTT@book> alter system dump datafile 4 block 748 ;
System altered.

$ sed -n "/Itl/,/bdba:/p" /u01/app/oracle/diag/rdbms/book/book/trace/book_ora_41566.trc
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0009.008.000036c8  0x00c00103.091d.0b  C---    0  scn 0x0003.157b2d98
0x02   0x000a.00c.0000d54b  0x00c002dd.2b3c.17  C---    0  scn 0x0003.157b37e2
0x03   0x000a.005.0000d554  0x00c002dd.2b3c.1a  C---    0  scn 0x0003.157b37e7
0x04   0x000a.007.0000d54e  0x00c002dd.2b3c.1d  C---    0  scn 0x0003.157b37ec
0x05   0x000a.01f.0000d554  0x00c002dd.2b3c.20  C---    0  scn 0x0003.157b37f1
0x06   0x000a.017.0000d549  0x00c002dd.2b3c.23  C---    0  scn 0x0003.157b37f6
0x07   0x000a.00a.0000d565  0x00c002dd.2b3c.26  C---    0  scn 0x0003.157b37fb
0x08   0x000a.01c.0000d51e  0x00c002dd.2b3c.29  C---    0  scn 0x0003.157b3800
0x09   0x000a.018.0000d55b  0x00c002dd.2b3c.2c  C---    0  scn 0x0003.157b3805
0x0a   0x000a.01a.0000d553  0x00c002dd.2b3c.2f  C---    0  scn 0x0003.157b380a
0x0b   0x000a.010.0000d549  0x00c002dd.2b3c.32  C---    0  scn 0x0003.157b380f
...
0xa5   0x000a.016.0000d559  0x00c002e3.2b3c.1a  C---    0  scn 0x0003.157b3b19
0xa6   0x0009.004.000036c8  0x00c00106.091d.10  C---    0  scn 0x0003.157b3b1f
0xa7   0x0007.010.000018cb  0x00c000e3.0661.21  C---    0  scn 0x0003.157b3b24
0xa8   0x000a.019.0000d555  0x00c002e3.2b3c.1d  C---    0  scn 0x0003.157b3b29
0xa9   0x0003.017.000010ed  0x00c0015d.0cda.0f  --U-    1  fsc 0x0000.157b3c6a
bdba: 0x010002ec

-- 0xa9 = 169. 總共佔用169個ITL槽.這也進一步證明8k的資料庫最多佔用169個ITL槽.

BBED> set dba 4,748
        DBA             0x010002ec (16777964 4,748)

BBED> map /v
File: /mnt/ramdisk/book/users01.dbf (4)
Block: 748                                   Dba:0x010002ec
------------------------------------------------------------
KTB Data Block (Table/Cluster)
struct kcbh, 20 bytes                      @0
    ub1 type_kcbh                           @0
    ub1 frmt_kcbh                           @1
    ub1 spare1_kcbh                         @2
    ub1 spare2_kcbh                         @3
    ub4 rdba_kcbh                           @4
    ub4 bas_kcbh                            @8
    ub2 wrp_kcbh                            @12
    ub1 seq_kcbh                            @14
    ub1 flg_kcbh                            @15
    ub2 chkval_kcbh                         @16
    ub2 spare3_kcbh                         @18

struct ktbbh, 4080 bytes                   @20
    ub1 ktbbhtyp                            @20
    union ktbbhsid, 4 bytes                 @24
    struct ktbbhcsc, 8 bytes                @28
    sb2 ktbbhict                            @36
    ub1 ktbbhflg                            @38
    ub1 ktbbhfsl                            @39
    ub4 ktbbhfnx                            @40
    struct ktbbhitl[169], 4056 bytes        @44

struct kdbh, 14 bytes                      @4108
    ub1 kdbhflag                            @4108
    sb1 kdbhntab                            @4109
    sb2 kdbhnrow                            @4110
    sb2 kdbhfrre                            @4112
    sb2 kdbhfsbo                            @4114
    sb2 kdbhfseo                            @4116
    sb2 kdbhavsp                            @4118
    sb2 kdbhtosp                            @4120

struct kdbt[1], 4 bytes                    @4122
    sb2 kdbtoffs                            @4122
    sb2 kdbtnrow                            @4124

sb2 kdbr[168]                              @4126

ub1 freespace[870]                         @4462

ub1 rowdata[2856]                          @5332

ub4 tailchk                                @8188

--可以發現freespace還有870,但是ITL槽的數量不在增加.

SCOTT@book> analyze table t list chained rows;
Table analyzed.

SCOTT@book> analyze table t compute statistics;
Table analyzed.

SCOTT@book> select table_name, num_rows, chain_cnt, avg_row_len from user_tables where table_name='T';
TABLE_NAME   NUM_ROWS  CHAIN_CNT AVG_ROW_LEN
---------- ---------- ---------- -----------
T                 734        729          17

3.繼續演示ITL不足的情況,如果有多個事務出現在該塊,由於要增加ITL槽,這樣必須搶佔freespace,這樣就不能保證記錄的擴充套件,實際上
oracle這個時候不允許增加itl槽,這樣就出現itl不足的情況.這也是我前次測試的結果:

--重來。
--drop table t purge ;
create table t (id number,pad varchar2(200));
alter table t pctfree 0;
create unique index pk_t on t(id);
alter table t add constraint pk_t primary key (id) enable validate;
insert into t(id)   select rownum from dual connect by level<=734;
commit ;

SCOTT@book> select rowid,id from t where id =1 or id=734 or id=733;
ROWID                      ID
------------------ ----------
AAAXW0AAEAAAALrAAA          1
AAAXW0AAEAAAALrALc        733
AAAXW0AAEAAAALsAAA        734

SCOTT@book> @ &r/rowid AAAXW0AAEAAAALrAAA
    OBJECT       FILE      BLOCK        ROW DBA                  TEXT
---------- ---------- ---------- ---------- -------------------- ----------------------------------------
     95668          4        747          0 4,747                alter system dump datafile 4 block 747 ;
BBED> map
File: /mnt/ramdisk/book/users01.dbf (4)
Block: 747                                   Dba:0x010002eb
------------------------------------------------------------
KTB Data Block (Table/Cluster)
struct kcbh, 20 bytes                      @0
struct ktbbh, 72 bytes                     @20
struct kdbh, 14 bytes                      @100
struct kdbt[1], 4 bytes                    @114
sb2 kdbr[733]                              @118
ub1 freespace[1579]                        @1584
ub1 rowdata[5025]                          @3163
ub4 tailchk                                @8188

--//可以發現freespace=1579 ,還有許多。

--session 1,注意不提交:
SCOTT@book(68,1377)> update t set pad=lpad('a',10,'a') where id=1;
1 row updated.

--session 2,注意不提交:
SCOTT@book(101,3247)> update t set pad=lpad('a',10,'a') where id=2;
1 row updated.

--session 3,注意不提交:
SCOTT@book(112,565)> update t set pad=lpad('a',10,'a') where id=3;

--//看看等待事件:

cat wait.sql
select p1raw,p2raw,p3raw,p1,p2,p3,sid,serial#,seq#,event,state,wait_time_micro,seconds_in_wait from v$session where wait_class<>'Idle' order by event ;

SCOTT@book> @ &r/wait
P1RAW            P2RAW            P3RAW                    P1         P2         P3        SID    SERIAL#       SEQ# EVENT                                    STATE               WAIT_TIME_MICRO SECONDS_IN_WAIT
---------------- ---------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------------------------------- ------------------- --------------- ---------------
0000000062657100 0000000000000001 00               1650815232          1          0        123        265         25 SQL*Net message to client                WAITED SHORT TIME                 2               0
0000000054580004 0000000000090005 00000000000036C9 1415053316     589829      14025        112        565         38 enq: TX - allocate ITL entry             WAITING                     8500417               9

--出現了enq: TX - allocate ITL entry等待事件,而這個時候透過bbed觀察,freespace=1579的情況.

總結:
1.這種情況在實際系統很少見,僅僅出現在行記錄很短的情況。
2.一旦發生行遷移,在被行遷移的塊中會增加至少1個ITL槽,當然我的測試非常極端,出現增加10-20個ITL槽應該是正常的。
3.這種情況另外一個副產品就是itl不足,當然也出現在塊寫很慢,行記錄很短的情況較常見。
4.從另外一個側面說明為什麼8K資料塊Hakan Factor=736 ,要保留行遷移的記錄nrid 6個位元組+前面3個
5.其實這些測試並不重要,主要在於更好的理解oracle的一些內部結構。

--補充測試:

)> update t set pad=lpad('a',10,'a') where id=4;
1 row updated.

SCOTT@book(112,565)> commit ;
Commit complete.

SCOTT@book(112,565)> alter system checkpoint ;
System altered.

BBED> set dba 4,747
        DBA             0x010002eb (16777963 4,747)

BBED> x /rnc *kdbr[2]
rowdata[9]                                  @3136
----------
flag@3136: 0x20 (KDRHFH)
lock@3137: 0x00
cols@3138:    0
nrid@3139:0x010002ee.1

BBED> x /rnc *kdbr[3]
rowdata[0]                                  @3127
----------
flag@3127: 0x20 (KDRHFH)
lock@3128: 0x01
cols@3129:    0
nrid@3130:0x010002ee.2

--//最少預留9個位元組。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/267265/viewspace-2122887/,如需轉載,請註明出處,否則將追究法律責任。

相關文章