您好,登錄后才能下訂單哦!
如何進行ITL與事務處理,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
一、ITL與事務的關系
ITL(interested transaction list)事務槽是Oracle數據塊內部的一個組成部分,位于數據塊頭(block header)。ITL由xid、uba、flag、lck和SCN/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中構造CR(consistent read )塊,此時Oracle也會檢查構造出來的block中ITL記錄的SCN,如果SCN仍然大于select時刻的SCN,那么將繼續重復構造前鏡像,直到前鏡像block中ITL記錄的SCN小于select時刻的SCN,同時檢查該事務是否提交或回滾,如果沒有,還要繼續構造前鏡像,直到找到需要的block。如果在構造前鏡像過程中所需的undo信息被覆蓋了,就會報快照過舊的錯誤。于是Oracle實現了多版本控制,這就是Oracle多版本的本質,這也就是為什么發出一條select語句時總是會看到consistent gets了。
二、ITL等待
發生ITL等待的場景有以下兩種情況:
1、超過了maxtrans配置的最大ITL數;
2、initrans配置不足,且沒有足夠的free space開擴展ITL。
解決辦法:
maxtrans不足:高并發引起,同一數據塊上的事務量已經超出了允許的ITL數量。因此需要減少事務的并發量,對于長事務,在保證數據完整性的前提下,增加commit的頻率,將長事務變為短事務,以減少資源占用。
initrans不足:數據塊上的ITL數量并沒有達到maxtrans的限制,發生這種情況的表通常是被較多的update,造成預留空間pctfree(默認10%)被填滿。此時可增加表的initrans或pctfree來解決,如果該表上事務的并發量高,可優先增加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表的數據占用了兩個數據塊,塊編號分別為171和174,t1段的第一個區間的起始編號為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,而pctfree為0,兩個事務不提交,block中就沒有足夠空間分配ITL了,因此出現了會話被hang住一直在等待ITL的分配。前面的會話提交或回滾后,后面的會話才得以執行。
四、ITL進一步研究
當一個事務完成時,Oracle需要執行塊清理(block cleanout),清理掉這些在數據塊上的事務數據,清除ITL中的標志位、行中row header中的標志位等。塊清理分為兩種:fast commit block cleanout和deferred 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狀態。
當下一次操作如select、update、insert或delete訪問到這些塊時再來完成對塊的清理,這稱之為延遲塊清理。塊延遲清除通過事務槽上的回滾段號、槽號等信息訪問回滾段頭的事務信息,若事務不再活躍或事務過期則完成塊清理。塊延遲清除的影響在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 committed(maybe long ago),SCN is an upper bound
-b--:this undo record contains the undo for this ITL entry
---t:transaction 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,三部分信息分別表示
xidusn:undo segment number 回滾段號
xidslot:slot number 事務槽號
xidsqn:sequence number 序列號
uba:其構成是dba.ubasqn.ubarec,而dba包含了ubafil和ubablk的信息,分解如下
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
ubafil:undo block address(uba) filenum 回滾塊地址 - 文件號
ubablk:undo block number 回滾塊地址 - 塊號
ubasqn:uba sequence number 回滾塊地址 - 序列號
ubarec:uba 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即序列號ubasqn,cnt即記錄號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 row,F和L分別表示行的first piece和last piece,說明此行涉及導出的數據塊,不存在行鏈接,又由于塊中存在行頭,說明也存在行遷移。
lb:ITL事務槽編號
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后來構建的),通過ITL和undo segment header中的transaction table,可以知道事務處于活動階段還是已經完成。事務在修改塊時會檢查row header中的標志位,如果該標志位為0(該行沒有被活動的事務鎖住,這是可能要進行延遲塊清除等工作),就把該標志位修改為事務在該塊獲得的ITL序號,這樣當前事務就獲得了對記錄的鎖定,然后就可以修改行數據了。與此同時,在該事務處理過程中,如果有會話查詢該數據塊中的數據,Oracle就會讀取回滾段中的內容來構造保障數據讀一致性的CR(consistent read)塊。
在多用戶并發環境下,一個數據塊可以有多個CR版本,Oracle會在下列情況下構造數據塊的CR版本:
1、如果一個數據塊上有鎖,而有會話需要讀取這個數據塊中的內容,Oracle就會構造該數據塊的CR版本;
2、是否需要構造CR塊,與SCN密切相關。如果一個查詢游標對應的SCN小于數據塊當前的SCN,此時Oracle需要構造對應查詢游標SCN的CR塊。
以下看一下數據塊及其不同版本的例子,操作分別在幾個不同會話中進行。
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
數據塊在緩存中的狀態及其物理意義如下:
free:not currently in use
xcur:exclusive
scur:shared current
cr:consistent read
read:begin read from disk
mrec:in media recovery mode
irec:in instance recovery mode
關于如何進行ITL與事務處理問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。