1. 沖突解決
假如有一個系統只有你和我兩個用戶,並且我們都在持續對系統中一小部分數據做修改和查詢操作。
如果你正在數據庫中做一批修改操作,而我正在做查詢,我一定不能看到你所做的修改,直到你告訴我可以看到你所做的所有更改才行(你提交了事務)。因此在oracle內部,必須有一個高效的辦法來識別哪些數據我可以看到,哪些數據我不可以看到。
從相反的角度來看,在你提交事務的時候,你需要一種高效的機制讓其他所有人能夠看到事務已經提交(也就是要告訴別人你所有修改過的數據都是可見的了)。更極端一點的情況是,你可能需要決定回滾事務,這樣的話,你也需要一種高效的機制能夠關聯上所有的undo記錄,按生成的順序排序,從而可以按相反的順序回滾這些更改。
2. 事務與undo
創建數據庫的時候,必須創建一個undo表空間。同時,oracle會在undo表空間中自動創建多個undo段,隨著數據庫負載的變化自動新增,擴大和收縮。
事務管理起始於undo段,並以此為中心。undo段的第一個塊(段頭塊)包含如下結構:擴展映射,擴展控制頭(跟其他類型的段頭塊一樣),事務表,事務控制區(特殊的結構)。事務表的大概結構如下:
TRN TBL:
index state cflags wrap# uel scn dba nub cmt
0X00 9 0X00 0X2013 0X001b 0X0000.016f1fc1 0X0180083e 0X00000001 1302762364
0X01 9 0X00 0X2014 0X001a 0X0000.016f1f54 0X0180083e 0X00000001 1302762364
0X02 10 0X80 0X2013 0X001d 0X0000.016f20fc 0X0180083e 0X00000001 0
0X03 9 0X00 0X200c 0X001c 0X0000.016f20d8 0X0180083e 0X00000001 1302762364
0X04 9 0X00 0X200f 0X001f 0X0000.016f1c75 0X0180083f 0X00000001 1302762364
.........
index 表示事務表中槽號,只是一個序列而已,從0x00開始到0x21結束,11g的版本有34個槽。
state 表示事務狀態:9代表事務不活動,10代表事務正在活動,從這裡我們看出16進制第0x02號槽上的事務正在活動。
cflags 表示正在使用事務槽的事務的狀態:0x00表示非活動事務、0x80表示活動事務、0x10表示死事務、0x90表示被回滾的死事務
wrap# 表示事務表上的事務槽被重用的次數,它是XID的一部分。0x2013表示此時事務槽被重用了8211次。
uel 表示當前活動事務所在事務槽的下一個事務槽的指針(即如果又發生一個新的事務,此時就會用到UEL指向的事務槽上的index)。
scn 表示務事啟動、提交、回滾的SCN.
dba 表示uba:第一部分的undo塊地址,這個DBA是(rollback)回滾的起始點,也就是說是記錄事務修改的最後一條記錄所在UNDO塊的地址。這使oracle在崩潰恢復時,能夠找到事務生成的最後一條undo記錄,以便知道從何處開始處理回滾。
nub 表示當前事務所用到的UNDO塊的個數。事務回滾時,可以看到該值會逐步減少。
cmt 表示最接近當前的提交時間戳,是從1970年1月1號零晨開始的(以秒為單位記錄)。0表示事務正在活動。
2.1 事務的開始和結束
當會話開始一個事務的時候,會先取到一個undo段,從undo段的事務表中取得一條記錄,然後增加該記錄的wrap#的值,將state改為active(10),同時修改事務表其他一些列(比如cmt置0)。由於這也是對於數據庫塊的修改,所以會生成一條最終寫入重做日志文件的重做改變向量(操作碼為5.2),並寫入數據庫。這樣會話就有了一個活動的事務了。
同樣,當事務完成時(通常是用戶commit),會將state設回為free(9),並更新其他一些列,比如將當期的SCN寫入scn列。同樣,對數據庫塊的這一修改也要生成一個重做改變向量(操作碼5.4),最終記錄到重做日志中。這一刻相當特別的重要,因為這個時刻你的會話正在向日志寫進程(lgwr)發布命令將日志緩沖區的當前內容寫入磁盤,並等待日志寫進程確認寫入完成,以保護所提交的更改。(這個發送命令要求lgwr進程輸出重做日志到磁盤其實很簡單,一旦發現進入日志緩沖區的重做日志操作碼是5.4,也就是是提交事務記錄,立刻將所有緩沖區的日志刷新輸出磁盤,完成後並告知把這條提交記錄放入日志緩沖區的會話。)
每一個事務會分配一個事務id,事務id由undo段編號,事務表中的條目索引號以及事務條目中最新的wrap#值構成。因此,當你看到像0X0009.002.00002013這樣的事務id時,就會知道:這個事務在undo段9裡,用的是第2條事務表記錄,wrap#值為0X2013。如果你想看這是哪個undo段,以及相應的段頭位置,可以使用segment_id列作為查詢條件查詢dba_rollback_segs視圖。
2.2 事務表
在上面我們已經介紹了事務表的結構了,現在回顧一下,事務表主要的目的無非下面幾個:
1. 顯示事務是已提交還是仍舊活動
2. 已提交事務的SCN
3. 事務生成的最近一條undo記錄的位置信息,便於回滾
4. 事務生成的undo量
如果一個事務必須回滾,或者一個會話被強制殺掉,使得smon(系統監視進程)必須要回滾它的事務,或者如果實例崩潰,在實例恢復期間,smon必須回滾所有崩潰時的活動事務。那麼此時可以輕松找到所有活動事務(狀態等於10),並且找到每一個事務正在使用的最後的undo塊(dba列)。然後可以根據每一個事務對應的undo塊鏈表往回查找,應用每一條undo記錄,因為每一條undo記錄指向了本事務中前一條undo記錄。
2.3 undo塊簡要介紹
undo塊和普通數據塊有許多相似之處:塊頭部分記錄了若干控制信息和元數據,行目錄列出了堆積存放在塊中的每一項的位置,若干undo記錄在塊中自底向上堆放在一起,塊空閒空間位於塊中間。但表行和undo記錄最大的區別在於,undo記錄不會被修改,因此一旦undo記錄被寫入塊中,會永遠保留在相同的位置。(表行被修改之後,如果原來位置放不下,會拷貝到另一個新位置,這樣塊中會留下雜亂的指針和臨時的空洞)。
一個不太為人知的事實是,單個undo塊也可以包含多個事務的undo記錄。一個事務會以獨占的方式請求undo塊的所有權,pin住,然後使用該塊直到塊滿(此時事務請求新undo塊並更新它的事務表槽以指向新塊)或事務提交。如果事務提交後,undo塊中仍有空閒空間,該塊會被加入到undo段頭中空閒塊池的候選鏈表中。如果發生這種情況,該undo塊會被其他事務使用其剩余的空間。