Oracle COMMIT之深入淺出
Oracle還是比較常用的,于是我研究了一下Oracle COMMIT,在這里拿出來和大家分享一下,希望對大家有用。只有當(dāng)SQL語句影響的所有行所在的***一個塊被讀入DB BUFFER并且重做信息被寫入REDO LOG BUFFER之后,用戶才可以發(fā)出COMMIT,Oracle COMMIT觸發(fā)LGRW,但并不強(qiáng)制立即DBWN來釋放所有相應(yīng)的DB BUFFER塊上的鎖,但在隨后的一段時間內(nèi)DBWN還在寫這條語句涉及的數(shù)據(jù)塊的情形,表頭部的行鎖,并不是在COMMIT一發(fā)出就馬上釋放,實(shí)際上要等到相應(yīng)的DBWN進(jìn)程結(jié)束才會釋放。
一個用戶請求鎖定另一個用戶已COMMIT的資源不成功的機(jī)會是存在的。Oracle COMMIT發(fā)出后會將回滾段中的"前映像"標(biāo)識為已提交.DML語句會產(chǎn)生一個SCN號,DBWN觸發(fā)時寫入到數(shù)據(jù)塊的頭部,COMMIT時也會產(chǎn)生一個SCN號,也會被寫入數(shù)據(jù)塊的頭部。在數(shù)據(jù)塊的頭部只存儲一個***的SCN號,COMMIT之后這個事務(wù)插槽可以被另外一個事務(wù)使用。如果用戶ROOLBACK,則服務(wù)器進(jìn)程會根據(jù)數(shù)據(jù)文件塊和DB BUFFER中塊的頭部的事務(wù)列表和SCN以及回滾段地址重構(gòu)出相應(yīng)的修改前的副本,并且用這些原值來還原當(dāng)前數(shù)據(jù)文件中已修改但未提交的改變。如果有多個"前映像",服務(wù)器進(jìn)程會在一個"前映像"的頭部找到"前前映像"的回滾段地址,一直重構(gòu)出同一事務(wù)下的最早的一個"前映像"為止。一旦發(fā)出了COMMIT,用戶就不能ROLLBACK,這使得COMMIT后DBWN進(jìn)程還沒有全部完成的后續(xù)動作得到了保障。
下面我們要提到檢查點(diǎn)的作用,ckpt的觸發(fā),有以下幾種情況:
1.當(dāng)發(fā)生日志組切換的時候
2.當(dāng)滿足log_checkpoint_timeout、log_checkpoint_interval、fast_start_io_target、fast_start_mttr_target參數(shù)設(shè)置的時候
3.當(dāng)運(yùn)行alter system switchlogfile的時候
4.當(dāng)運(yùn)行alter systemckeckpoint的時候
5.當(dāng)運(yùn)行altertablespacetbs_namebegin backup[end backup]的時候
6.當(dāng)運(yùn)行altertablespace[datafile] offline的時候
7.系統(tǒng)正常關(guān)閉時
只有在4.7兩種情況下發(fā)生完全檢查點(diǎn)。發(fā)生完全檢查點(diǎn)時,首先系統(tǒng)記錄檢查點(diǎn)對應(yīng)的Checkpoint SCN,并記錄下該時刻修改的DB BUFFER對應(yīng)的日志文件的***的重做字節(jié)地址(Redo Byte Address (RBA)),然后DBWN進(jìn)程將這個重做字節(jié)地址(RBA)之前已發(fā)生的DB BUFFER中的臟緩沖寫入數(shù)據(jù)文件(之所以要以重做字節(jié)地址(RBA)為標(biāo)志是因?yàn)樵跈z查點(diǎn)發(fā)生到檢查點(diǎn)完成之間的時間內(nèi),系統(tǒng)還在一直不斷的產(chǎn)生修改,這些修改所產(chǎn)生的DB BUFFER臟緩沖,以及日志條目將不會影響這次檢查點(diǎn)***確認(rèn)的一致性結(jié)果,也就是***確認(rèn)這個Checkpoint SCN之前的系統(tǒng)是一致的)。
***把Checkpoint SCN和RBA更新至控制文件,Checkpoint SCN更新至每個數(shù)據(jù)文件頭部,表明當(dāng)前數(shù)據(jù)庫是一致的。日志切換并不導(dǎo)致一個完全檢點(diǎn)的發(fā)生,比如有三個日志文件組,當(dāng)發(fā)生日志切換時發(fā)生檢查點(diǎn),而發(fā)生日志切換一般是因?yàn)楫?dāng)前的LGWR正在寫重做日志,也就是LGWR當(dāng)剛寫滿2號日志就立即觸發(fā)檢查點(diǎn),于是系統(tǒng)開始核對3號日志中記錄的REDO項(xiàng)目所對應(yīng)的數(shù)據(jù)是否已經(jīng)從DB BUFFER中寫入數(shù)據(jù)文件(不管事務(wù)是否已提交),如果沒有寫入,檢查點(diǎn)就觸發(fā)DBWN進(jìn)程將這些緩沖塊寫入數(shù)據(jù)文件,顯然LGWR因此而發(fā)生等待,除此以外,檢查點(diǎn)還讓DBWN進(jìn)程將在2號日志中對應(yīng)修改的DB BUFFER塊寫入數(shù)據(jù)文件,然后繼續(xù)LGWR進(jìn)程,直到LGWR進(jìn)程將LGWR觸發(fā)之前存在于REDO LOG BUFFER中的所有緩沖(包含未提交的重做信息)寫入重做日志文件,檢查點(diǎn)再更新數(shù)據(jù)文件,控制文件頭部SCN。其實(shí)LGWR等待的并不是CKPT的完成,而是等待CKPT觸發(fā)的DBWN進(jìn)程的完成。
可以想像斷電時可能既有未COMMIT的事務(wù),也可能同時存在已COMMIT但DBWN未完成的情況,如果斷電時有一個已COMMIT但DBWN動作沒有完成的情況存在,因?yàn)橐呀?jīng)COMMIT,COMMIT會觸發(fā)LGWR進(jìn)程,所以不管DBWN動作是否已完成,該語句將要影響的行及其產(chǎn)生的結(jié)果一定已經(jīng)記錄在重做日志文件中了,則實(shí)例重啟后,SMON進(jìn)程從控制文件中記錄的上一次重做字節(jié)地址(RBA)開始,按照重做日志文件中的條目對數(shù)據(jù)文件和回滾段重新做一遍即前滾,注意這些條目的操作在斷電之前有的已經(jīng)被DBWN寫入了數(shù)據(jù)文件,有的還沒有來得及寫,不管有沒有寫進(jìn)數(shù)據(jù)文件,前滾時都會再重新寫一次(9I之前是這樣的),9I之后,由于也在日志中記錄了DBWN改寫的塊信息,系統(tǒng)會過濾掉已寫入的條目而只重做那些未寫入的條目。對于一個未提交事務(wù),分幾種情況來描述:
1)LGWR與DBWN一致的情況即一個語句執(zhí)行完成后很長時間也沒有COMMIT,這種情況一般不存在DBWN來不及完成的情況。只是沒有Oracle COMMIT而已。那么SMON將在前滾完成后,利用回滾段重構(gòu)出具有最小SCN的前映像,并把它的值寫回原位。
2)事務(wù)執(zhí)行中斷電,即可能存在LGWR與DBWN不同步的情況(因?yàn)镈BWN之前會觸發(fā)LGWR,所以DBWN對數(shù)據(jù)文件的修改一定會被先記錄在重做日志文件中。因此只可能存在已寫入重做日志而未來得及寫入數(shù)據(jù)文件的情況存在。而不可能存在已寫入數(shù)據(jù)文件卻沒有寫入日志文件的情況。),這種情況下SMON也會先前滾一點(diǎn)(即把數(shù)據(jù)文件與相應(yīng)的日志文件先同步再回滾,之所以說前滾一點(diǎn),是指僅LGWR與DBWN之間進(jìn)度的差距,而不是把這條語句進(jìn)行到底再回滾,因?yàn)槿罩疚募杏涗浀氖菆?zhí)行語句操作的一個個塊的修改信息,而不只是記錄一條執(zhí)行語句的字面內(nèi)容),然后利用回滾段重構(gòu)出具有最小SCN的前映像,并把它的值寫回原位。由此可見,實(shí)例失敗后用于恢復(fù)的時間由兩個檢查點(diǎn)之間的間隔大小來決定,我們可以通個四個參數(shù)設(shè)置檢查點(diǎn)執(zhí)行的頻率,LOG_CHECKPOINT_IMTERVAL決定了兩個檢查點(diǎn)之間寫入重做日志文件的系統(tǒng)物理塊的大小,LOG_CHECKPOINT_TIMEOUT決定了兩個檢查點(diǎn)之間的時間長度,F(xiàn)AST_START_IO_TARGET決定了用于恢復(fù)時需要處理的塊的大小,F(xiàn)AST_START_MTTR_TARGET直接決定了用于恢復(fù)的時間的長短。
檢查點(diǎn)的作用就是不斷的確認(rèn)LGWR與DBWN之間的同步情況,以便實(shí)例失敗后從上一個檢查點(diǎn)開始恢復(fù),問題是兩個檢查點(diǎn)之間LGWR與DBWN大部分的操作是同步的,只是一小部分沒有同步,這種傳統(tǒng)的檢查點(diǎn)使實(shí)例恢復(fù)做了比較多的無用功,因此,ORACLE引入了增量檢查點(diǎn),增量檢查點(diǎn)會在上一次傳統(tǒng)檢查點(diǎn)發(fā)生后到下一次傳統(tǒng)檢查點(diǎn)發(fā)生之前,不斷的更新記錄在控制文件中重做字節(jié)地址(RBA)(CKPT進(jìn)程每三秒更新一次,見下面DBWN講述),這樣實(shí)例失敗后將直接從控制文件中記錄的***更新的重做字節(jié)地址(RBA)開始進(jìn)行前滾和回滾,這就省略掉了恢復(fù)時大部份的重做日志的重做(即使在9I以后的版本里也省略掉了大部分的過濾重做日志條目的時間)。(對以上描述做一個簡單的比喻:比如一個貿(mào)易公司下設(shè)經(jīng)營部、貨運(yùn)部、監(jiān)督部,經(jīng)營部負(fù)責(zé)貿(mào)易合同的簽訂與記錄,貨運(yùn)部負(fù)責(zé)按合同號的順序把貨物送達(dá),監(jiān)督部負(fù)責(zé)定期檢查確認(rèn)經(jīng)營部簽訂的合同與貨運(yùn)部貨物送達(dá)情況之間的同步情況,監(jiān)督部每月檢查一次,每次檢查時,先確認(rèn)當(dāng)時正在裝車的貨物的合同號,并要求貨運(yùn)部把在這個合同號之前的所有還存在臨時倉庫中的未送貨物全部送達(dá)。等貨運(yùn)部完成監(jiān)督部下達(dá)的任務(wù)后,監(jiān)督部在檢查本上記錄下本次開始檢查時那票正在裝車的貨物的合同號,本次檢查完成。如果這個公司發(fā)生了一次人事大換血,公司重新開業(yè)后,監(jiān)督部就會從檢查本上記錄的合同號開始,檢查在這之后所有發(fā)生的合同及貨物送達(dá)情況,要求貨運(yùn)部把所有客戶確認(rèn)的但還未送達(dá)的貨物送達(dá)。以上介紹Oracle COMMIT。
【編輯推薦】