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
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28974745/viewspace-2146505/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 分散式事務處理方案,微服事務處理方案分散式
- mysql事務處理與鎖機制MySql
- MySQL事務處理MySql
- 12事務處理
- mysqli 事務處理MySql
- ITL事務槽記載-01
- redis的事務處理Redis
- php事務處理方法PHP
- java事務的處理Java
- MYSQL--事務處理MySql
- SpringDataRedis事務處理SpringRedis
- mysql事務處理(轉)MySql
- zt_ITL Cleanout和ITL中的事務狀態分析
- Oracle ITL (Interested Transaction List) - 事務槽OracleREST
- Laravel 分散式事務處理Laravel分散式
- springboot事務處理Spring Boot
- Spring (二) 事務處理Spring
- [MYSQL -26]控制事務處理MySql
- 分散式事務故障處理分散式
- JDBC事務處理設計JDBC
- MySQL中的事務處理MySql
- SQL SERVER 事務處理(一)SQLServer
- sql server 事務處理(二)SQLServer
- 事務處理基本概念
- 【Mysql】mysql事務處理用法與例項詳解MySql
- JDBC 事務處理【最終版】JDBC
- Redis中的事務處理機制分析與總結Redis
- Spring中使用@Async與@Transactional協調非同步與事務處理Spring非同步
- Spring事務專題(三)事務的基本概念,Mysql事務處理原理SpringMySql
- springcloud分散式事務處理 LCNSpringGCCloud分散式
- Oracle分散式事務典型案例處理Oracle分散式
- Entity Framework中 批量提交 事務處理Framework
- C#處理Access中的事務C#
- 【開發篇plsql】plsql事務處理SQL
- 軟體中事務處理問題!
- 關於jdon 的事務處理疑惑?
- spring事務管理原始碼分析(二)事務處理流程分析Spring原始碼
- 我的MySql事務處理(可以支援事務處理及資料庫路徑自己定義) (轉)MySql資料庫