ITL與事務處理

llnnmc發表於2017-10-27

一、ITL與事務的關係

 

ITLinterested transaction list)事務槽是Oracle資料塊內部的一個組成部分,位於資料塊頭(block header)。ITLxidubaflaglckSCN/fsc組成,用來記錄在該資料塊上所有發生的事務。一個ITL槽位可以看作是一條事務記錄,它是Oracle中事務處理的關鍵元件,如果事務已經提交,則該ITL槽位就可以被反覆使用,如果一直不提交,則該ITL槽位一直被佔用,裡面記錄著事務資訊、回滾段入口、事務型別等。事務提交後,ITL槽位中仍儲存著該事務提交時的SCN號。

 

ITL最小值為1,由引數initrans控制(由於相容性的原因,Oracle會在物件的儲存塊上分配兩個ITL,因此inittrans的最小值實際上為2),這也是在建表時如果不指定initrans引數時的預設取值,最大值為255,由引數maxtrans控制,最大值引數在Oracle 10g以後不能被修改。一個ITL佔用塊46B的空間,當塊中還有一定的free space時,Oracle可以使用free space構建ITL供事務使用,如果沒有了free space,則塊因為不能分配新的ITL就可能發生ITL等待。

 

當使用者發出一條SQL語句時,Oracle會記錄下這個時刻的SCN,然後在buffer cache中查詢需要的block,或者從磁碟上讀取,當別的會話修改了資料,或者正在修改資料,就會在相應的block上記錄ITL,此時Oracle發現ITL中記錄的SCN大於select時刻的SCN,那麼Oracle就會根據ITL中記錄的uba找到undo資訊,獲得該block的前映象,然後在buffer cache中構造CRconsistent read )塊,此時Oracle也會檢查構造出來的blockITL記錄的SCN,如果SCN仍然大於select時刻的SCN,那麼將繼續重複構造前映象,直到前映象blockITL記錄的SCN小於select時刻的SCN,同時檢查該事務是否提交或回滾,如果沒有,還要繼續構造前映象,直到找到需要的block。如果在構造前映象過程中所需的undo資訊被覆蓋了,就會報快照過舊的錯誤。於是Oracle實現了多版本控制,這就是Oracle多版本的本質,這也就是為什麼發出一條select語句時總是會看到consistent gets了。

 

二、ITL等待

 

發生ITL等待的場景有以下兩種情況:

1、超過了maxtrans配置的最大ITL數;

2initrans配置不足,且沒有足夠的free space開擴充套件ITL

 

解決辦法:

 

maxtrans不足:高併發引起,同一資料塊上的事務量已經超出了允許的ITL數量。因此需要減少事務的併發量,對於長事務,在保證資料完整性的前提下,增加commit的頻率,將長事務變為短事務,以減少資源佔用。

 

initrans不足:資料塊上的ITL數量並沒有達到maxtrans的限制,發生這種情況的表通常是被較多的update,造成預留空間pctfree(預設10%)被填滿。此時可增加表的initranspctfree來解決,如果該表上事務的併發量高,可優先增加initrans,增大ITL槽位的初始分配量,反之,則優先增加pctfree,提升ITL槽位的擴充套件能力。

 

注意:如果是透過alter table方式修改了表的這兩個引數,那麼只會影響新的資料塊,而不會改變已有資料的資料塊。

 

三、實驗驗證ITL與事務的關係

 

連線到scott使用者

sqlplus scott/tiger

 

建立測試表,pctfree設為0

create table t1(a number, b varchar2(30)) pctfree 0;

begin

    for i in 1 .. 1000 loop

        insert into t1 values (i, 'data');

    end loop;

    commit;

end;

/

 

檢視段的區間分配資訊

col segment_name for a20

col tablespace_name for a20

select segment_name, segment_type, tablespace_name, extent_id, file_id, block_id, blocks, bytes from dba_extents where owner = 'SCOTT' and segment_name = 'T1';

SEGMENT_NAME         SEGMENT_TYPE       TABLESPACE_NAME       EXTENT_ID    FILE_ID   BLOCK_ID     BLOCKS      BYTES

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

T1                   TABLE              USERS                         0          4        168          8      65536

 

檢視塊分配資訊

select distinct dbms_rowid.rowid_block_number(rowid) from scott.t1;

 

DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

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

                                 171

                                 174

 

由此可知,t1表的資料佔用了兩個資料塊,塊編號分別為171174t1段的第一個區間的起始編號為168,該區間由8個資料塊組成。

 

下面在同一個資料塊171上同時執行多個事務,看看到底會發生什麼。

 

session1

 

update scott.t1 set b = 'Oracle data' where a <= 10;

 

已更新10行。

 

session2

 

update scott.t1 set b = 'Oracle data' where a > 10 and a <= 20;

 

已更新10行。

 

session3

 

先確定當前會話的sid

select sid from v$mystat where rownum = 1;

 

       SID

----------

       136

 

update scott.t1 set b = 'Oracle data' where a > 20 and a <= 30;

 

操作被hang住,事務處於等待狀態。

 

檢視會話的等待事件

col event for a30

select sid, event, seconds_in_wait, state from v$session_wait where sid = 136;

 

       SID EVENT                          SECONDS_IN_WAIT STATE

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

       136 enq: TX - allocate ITL entry               242 WAITING

 

此時出現了分配ITL條目的等待。因為預設的初始ITL槽位分配為2,而pctfree0,兩個事務不提交,block中就沒有足夠空間分配ITL了,因此出現了會話被hang住一直在等待ITL的分配。前面的會話提交或回滾後,後面的會話才得以執行。

 

四、ITL進一步研究

 

當一個事務完成時,Oracle需要執行塊清理(block cleanout),清理掉這些在資料塊上的事務資料,清除ITL中的標誌位、行中row header中的標誌位等。塊清理分為兩種:fast commit block cleanoutdeferred block cleanout

 

快速提交塊清理(fast commit block cleanout):這是Oracle的預設行為。

延遲塊清理(deferred block cleanout):事務提交時,Oracle僅簡單的更新相關回滾段的頭部資訊,而把資料塊的清理操作留給後來需要讀寫這個資料塊的操作者(之後的事務)。

 

設想一個update大量資料的操作,因為執行時間較長,一部分已修改的塊已被緩衝池flush out寫至磁碟,當update操作完成執行commit操作時,為進行塊清理,需要將那些已經寫至磁碟的資料塊重新讀入,這將消耗大量I/O,並使commit操作十分緩慢。為解決這個問題,Oracle使用了延遲塊清理的方案,對待存在以下情況的塊,commit操作不做塊清理:

1、在更新過程中,被緩衝池flush out寫至磁碟的塊;

2、當更新操作涉及的塊超過了塊緩衝區快取的10%時,超出部分的塊。

 

雖然commit放棄對這些塊的清理,但仍會修改回滾段的段頭,回滾段的段頭包括了段中的事務資訊,commit操作將本事務轉化為非active狀態。

當下一次操作如selectupdateinsertdelete訪問到這些塊時再來完成對塊的清理,這稱之為延遲塊清理。塊延遲清除透過事務槽上的回滾段號、槽號等資訊訪問回滾段頭的事務資訊,若事務不再活躍或事務過期則完成塊清理。塊延遲清除的影響在select操作過程中體現的最為明顯,這也是select語句產生redo資訊的主要原因。

 

繼續前面的案例,先執行一個更新啟動一個新的事務,然後查詢出此更新涉及的資料塊,然後dump該資料塊的內容,進一步驗證ITL的資訊。

 

執行更新

update scott.t1 set b = 'Oracle data' where a = 100;

 

確定更新所在的檔案號和塊號

select dbms_rowid.rowid_relative_fno(rowid) from scott.t1 where a = 100;

 

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)

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

                                   4

 

select dbms_rowid.rowid_block_number(rowid) from scott.t1 where a = 100;

 

DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

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

                                 171

 

開啟會話跟蹤

alter session set sql_trace=true;

oradebug setmypid

oradebug tracefile_name

c:\oracle\diag\rdbms\mes\mes\trace\mes_ora_3852.trc

 

dump資料塊

alter system dump datafile 4 block 171;

 

檢視跟蹤檔案c:\oracle\diag\rdbms\mes\mes\trace\mes_ora_3852.trc,可以看到關於ITL的資訊:

Block header dump:  0x010000ab

 Object id on Block? Y

 seg/obj: 0x12458  csc: 0x00.1eb08d  itc: 2  flg: E  typ: 1 - DATA

     brn: 0  bdba: 0x10000a8 ver: 0x01 opc: 0

     inc: 0  exflg: 0

 

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc

0x01   0x0007.015.0000040c  0x00c00618.017a.07  C-U-    0  scn 0x0000.001ea944

0x02   0x0006.00e.000004ee  0x00c011d1.014e.1e  ----    1  fsc 0x0002.00000000

 

flag:事務狀態標誌,佔用塊中的一個位元組,對應於v$transaction檢視中的status欄位,意義如下:

----transaction is active or committed pending cleanout

c---transaction has been committed and locks cleaned out

--u-transaction committedmaybe long ago),SCN is an upper bound

-b--this undo record contains the undo for this ITL entry

---ttransaction was still active at block cleanout SCN

 

SCN/fsc:該ITL對應的事務提交時的SCN,那麼這裡所有槽位上最大的一個SCN號就表示這個block最後被更新時的SCN。每一個事務對應一個ITL記錄,如果該事務沒有涉及延遲塊清理,那麼顯示的是fsc,如果是延遲塊清理,那麼顯示的就是SCN

 

lck:事務鎖影響的記錄數。

 

對照檢視v$transaction,獲取此處update操作對應的事務資訊

select xidusn, xidslot, xidsqn, ubafil, ubablk, ubasqn, ubarec from v$transaction;

 

    XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK     UBASQN     UBAREC

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

         6         14       1262          3       4561        334         30

 

xid:其構成是xidusn.xidslot.xidsqn,三部分資訊分別表示

xidusnundo segment number 回滾段號

xidslotslot number 事務槽號

xidsqnsequence number 序列號

 

uba:其構成是dba.ubasqn.ubarec,而dba包含了ubafilubablk的資訊,分解如下

select dbms_utility.data_block_address_file(to_number('00c011d1', 'xxxxxxxx')) ubafil, dbms_utility.data_block_address_block(to_number('00c011d1', 'xxxxxxxx')) ubablk from dual;

 

    UBAFIL     UBABLK

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

         3       4561

 

ubafilundo block addressuba filenum 回滾塊地址 - 檔案號

ubablkundo block number 回滾塊地址 - 塊號

ubasqnuba sequence number 回滾塊地址 - 序列號

ubarecuba record number 回滾塊地址 - 記錄號

 

於是根據uba資訊,可以從回滾段的資料塊中找到該項事務的回滾資訊,為此可以dump回滾段的資料塊

alter system dump datafile 3 block 4561;

 

檢視跟蹤檔案資訊,找到該事務對應的回滾資訊

UNDO BLK: 

xid: 0x0006.00e.000004ee  seq: 0x14e cnt: 0x1e  irb: 0x1e  icl: 0x0   flg: 0x0000

 

這裡的seq即序列號ubasqncnt即記錄號ubarec。由rec #0x1e可以進一步在trace檔案中找到update前的回滾資訊

 

* Rec #0x1e  slt: 0x0e  objn: 74840(0x00012458)  objd: 74840  tblspc: 4(0x00000004)

*       Layer:  11 (Row)   opc: 1   rci 0x1d  

Undo type:  Regular undo   Last buffer split:  No

Temp Object:  No

Tablespace Undo:  No

rdba: 0x00000000

*-----------------------------

KDO undo record:

KTB Redo

op: 0x02  ver: 0x01 

compat bit: 4 (post-11) padding: 0

op: C  uba: 0x00c011d1.014e.1c

KDO Op code: ORP row dependencies Disabled

  xtype: XA flags: 0x00000000  bdba: 0x010000ab  hdba: 0x010000aa

itli: 2  ispac: 0  maxfr: 4858

tabn: 0 slot: 99(0x63) size/delt: 11

fb: --H-FL-- lb: 0x2  cc: 2

null: --

col  0: [ 2]  c2 02

col  1: [ 4]  64 61 74 61

 

fb:行標記,H表示head of rowFL分別表示行的first piecelast piece,說明此行涉及匯出的資料塊,不存在行連結,又由於塊中存在行頭,說明也存在行遷移。

lbITL事務槽編號

cc:列的數量

 

回滾前的編碼是64 61 74 61,轉換為原始字元資訊

select chr(to_number(64, 'xx')) || chr(to_number(61, 'xx')) || chr(to_number(74, 'xx')) || chr(to_number(61, 'xx')) undo_data from dual;

 

UNDO_DAT

--------

data

 

以上測試可見,Oracle是透過資料塊中的ITL資訊來找到事務對應的回滾資訊,同時實現了事務的讀一致性。如果事務已完成,ITL就可以被重用。

 

五、ITL與CR塊

 

Oracle的鎖管理是一種輕量級的鎖定機制,不是透過構建鎖列表來進行資料鎖定管理的,而是直接將鎖作為資料塊的屬性儲存在資料塊頭部,透過ITL實現。一個事務要修改塊中的資料,必須獲得改塊中的一個ITL(透過initrans預先分配的或者是透過pctfree space後來構建的),透過ITLundo segment header中的transaction table,可以知道事務處於活動階段還是已經完成。事務在修改塊時會檢查row header中的標誌位,如果該標誌位為0(該行沒有被活動的事務鎖住,這是可能要進行延遲塊清除等工作),就把該標誌位修改為事務在該塊獲得的ITL序號,這樣當前事務就獲得了對記錄的鎖定,然後就可以修改行資料了。與此同時,在該事務處理過程中,如果有會話查詢該資料塊中的資料,Oracle就會讀取回滾段中的內容來構造保障資料讀一致性的CRconsistent read)塊。

 

在多使用者併發環境下,一個資料塊可以有多個CR版本,Oracle會在下列情況下構造資料塊的CR版本:

1、如果一個資料塊上有鎖,而有會話需要讀取這個資料塊中的內容,Oracle就會構造該資料塊的CR版本;

2、是否需要構造CR塊,與SCN密切相關。如果一個查詢遊標對應的SCN小於資料塊當前的SCN,此時Oracle需要構造對應查詢遊標SCNCR塊。

 

以下看一下資料塊及其不同版本的例子,操作分別在幾個不同會話中進行。

 

session1

查出表中資料所在的檔案號和塊號

select distinct dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block# from scott.emp;

 

     FILE#     BLOCK#

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

   4             151

 

由檔案號和塊號查詢快取中的資料塊,此時還沒有該資料塊資訊

select file#, block#, status, dirty, objd, ts# from v$bh where file# = 4 and block# = 151;

 

未選定行

 

對資料做查詢操作

select * from scott.emp;

 

快取中產生了資料塊的xcur版本

select file#, block#, status, dirty, objd, ts# from v$bh where file# = 4 and block# = 151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 xcur       N      73196          4

 

重新整理快取

alter system flush buffer_cache;

 

快取中的資料塊沒有消失,但狀態變為了free版本

select file#, block#, status, dirty, objd, ts# from v$bh where file# = 4 and block# = 151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

 

對資料再次做查詢操作

select * from scott.emp;

 

查詢快取塊,此時多了一個xcur版本

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

         4        151 xcur       N      73196          4

 

session2

對資料塊進行更新操作,但不提交

update scott.emp set sal = 1000 where empno =7369;

 

session1

查詢快取塊,dirty列標誌為Y,表示為髒資料

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

 

再次對資料做查詢操作

select * from scott.emp;

 

查詢快取塊,又多了一個cr版本,因為之前session2的更新沒有提交,所以session1查詢時,透過讀取回滾段在buffer cache中構建了CR塊。

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

 

session2

提交更新

commit;

 

session1

再次對資料做查詢操作

select * from scott.emp;

 

查詢快取塊,狀態資訊不變,因為是否提交併不影響資料塊內容

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

 

session2

再次更新資料,但不提交

update scott.emp set sal = 800 where empno =7369;

 

session1

對資料做查詢操作

select * from scott.emp;

 

查詢快取塊,可以看到又新構建了一個CR版本

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

         4        151 cr         N      73196          4

 

session2

提交更新

commit;

 

session1

對資料做查詢操作

select * from scott.emp;

 

查詢快取塊,狀態不改變

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

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

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

         4        151 cr         N      73196          4

 

資料塊在快取中的狀態及其物理意義如下:

freenot currently in use

xcurexclusive

scurshared current

crconsistent read

readbegin read from disk

mrecin media recovery mode

irecin instance recovery mode

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

相關文章