Oracle DB BUFFER寶典說明
經(jīng)過長時間學(xué)習(xí)Oracle,于是和大家分享一下,看完本文你肯定有不少收獲,希望本文能教會你更多東西。生成編譯代碼之后,接著下一步服務(wù)器進(jìn)程要準(zhǔn)備開始更新數(shù)據(jù),服務(wù)器進(jìn)程將到Oracle DB BUFFER中查找是否有相關(guān)對象的緩存數(shù)據(jù),下面分兩個可能進(jìn)行解釋:
如果沒有,服務(wù)器進(jìn)程將在表頭部請求一些行鎖,如果成功加鎖,服務(wù)器進(jìn)程將從數(shù)據(jù)文件中讀入這些行所在的***個數(shù)據(jù)塊(db block)(DB BLOCK是ORACLE的最小操作單元,即使你想要的數(shù)據(jù)只是DB BLOCK中很多行中的一行或幾行,ORACLE也會把這個DB BLOCK中的所有行都讀入Oracle DB BUFFER中)放入Oracle DB BUFFER中空閑的區(qū)域或者覆蓋已被擠出LRU列表的非臟數(shù)據(jù)塊緩沖區(qū),并且排列在LRU列表的頭部,如果這些非臟數(shù)據(jù)緩沖區(qū)寫完也不能滿足新數(shù)據(jù)的請求時,會立即觸發(fā)DBWN進(jìn)程將臟數(shù)據(jù)列表中指向的緩沖塊寫入數(shù)據(jù)文件,并且清洗掉這些緩沖區(qū),來騰出空間緩沖新讀入的數(shù)據(jù),也就是在放入Oracle DB BUFFER之前也是要先申請Oracle DB BUFFER中的鎖存器,成功鎖定后,再寫入Oracle DB BUFFER,然后把這個塊的頭部事務(wù)列表及SCN信息及被影響的行數(shù)據(jù)原值寫入回滾段中,以便ORACLE在ROLLBACK時可以利用當(dāng)前數(shù)據(jù)塊和回滾段重構(gòu)數(shù)據(jù)塊的"前映像"或遞歸重構(gòu)出"前…前映像"來實(shí)現(xiàn)讀一致性。
然后在LOG BUFFER中生成日志,服務(wù)器程將該語句影響的被讀入Oracle DB BUFFER塊中的這些行的ROWID及將要更新的原值和新值及SCN等信息,以及回滾段的修改信息(即對某某回滾段地址進(jìn)行了什么修改)逐條的寫入REDO LOG BUFFER,在寫入REDO LOG BUFFER之前也是先請求REDO LOG BUFFER塊的鎖存器,成功鎖定之后才開始把REDOLOG寫入REDOLOG BUFFER。當(dāng)寫入達(dá)到REDO LOG BUFFER大小的三分之一或?qū)懭肓窟_(dá)到1M或超過三秒后或發(fā)生檢查點(diǎn)時或者COMMIT時或者DBWN之前觸發(fā)LGWR進(jìn)程,LGWR將把REDO LOG BUFFER中的數(shù)據(jù)寫入磁盤上的重做日志文件,已被寫入重做日志文件的REDO LOG BUFFER中的塊上的鎖存器被釋放,并可被后來寫入的信息所覆蓋。
回滾段其實(shí)也有BUFFER(在Oracle DB BUFFER中開辟),回滾段BUFFER中的內(nèi)容是最早向磁盤上回滾段中寫的,寫完這些才會生成日志BUFFER中的內(nèi)容,原因是日志中必須要記錄回滾段的新舊變化以便在恢復(fù)時從日志中的記錄的回滾段新舊變化對回滾段再次重寫,記住,REDO不光是對數(shù)據(jù)文件依據(jù)日志文件重寫,也要依據(jù)日志文件對回滾段重寫,而且重寫回滾段要先于重寫數(shù)據(jù)文件,要理解REDO就是重來一遍,所謂重來一遍就要跟正常的的先后順利一樣重做一遍(正常的操作中的順序就是先讀入Oracle DB BUFFER,寫回滾段buffer,后寫回滾段,后寫日志BUFFER,后改寫Oracle DB BUFFER,后寫日志***寫數(shù)據(jù)文件)區(qū)別是REDO時不用再記日志了,這樣解釋后相信大家應(yīng)該理解為什么日志中也必須要記錄回滾段的信息了,只有這樣才可以對正常操作中的一個ROLLBACK動作進(jìn)行恢復(fù),即在REDO過程中利用即時重寫的數(shù)據(jù)塊和回滾段重構(gòu)出一個當(dāng)時適用的前鏡像來rollback。當(dāng)一個重做日志文件寫滿后,LGWR將切換到下一個重做日志文件,重做日志文件也是循環(huán)工作方式。如果是歸檔模式,歸檔進(jìn)程還將前一個寫滿的重做日志進(jìn)程寫入歸檔日志文件。
當(dāng)Oracle DB BUFFER改寫之后,服務(wù)器進(jìn)程在臟數(shù)據(jù)列表中建立一條指向此Oracle DB BUFFER緩沖塊的指針。接著服務(wù)器進(jìn)程會從數(shù)據(jù)文件讀入第二個數(shù)據(jù)塊(db block)重復(fù)以上讀入,建立回滾段,寫LOG BUFFER,改寫Oracle DB BUFFER,放入臟列表的動作,當(dāng)臟數(shù)據(jù)列表達(dá)到一定長度時,DBWN進(jìn)程將臟數(shù)據(jù)列表中指向的緩沖塊全部寫入數(shù)據(jù)文件,也就是釋放加在這些DB BUFER塊上的鎖存器,并在修改相應(yīng)塊的頭部的SCN號(一次UPDATE操作只對應(yīng)一個SCN)。前面說過DBWN動作之前會先觸發(fā)LGWR,這用以確保寫入數(shù)據(jù)文件的改變首先會被記錄在日志文件中。實(shí)際上ORACLE可以從數(shù)據(jù)文件中一次讀入多個塊放入Oracle DB BUFFER,然后再對這些塊建回滾段、再記日志等等,也就是每次操作的對象是DB BLOCK的復(fù)數(shù),而不僅限于一次操作一個DB BLOCK,可以通過參數(shù)DB_FILE_MULTIBLOCK_READ_COUNT來設(shè)置一次讀入的塊的個數(shù)。注意,不管是否提交,用戶的所有更改都會被記錄在日志文件中,用戶級回滾的動作(rollback)沒有對應(yīng)的COMMIT SCN。
在密集事務(wù)的情況下,LGWR可以把多個COMMIT產(chǎn)生的REDO條目批量寫入REDO LOG FILE,但每個COMMIT之間有十分之一秒的間隔,且會產(chǎn)生不同的COMMIT SCN。LGWR正常情況下是一個休眠進(jìn)程,會被一定的條件觸發(fā),喚醒,比如COMMIT就是一個喚醒條件,一旦LGWR被喚醒,LGWR將把喚醒時間點(diǎn)之前LOG BUFFER中產(chǎn)生的所有內(nèi)容(從上次LGWR喚醒后到本次喚醒前之間寫入LOG BUFFER的內(nèi)容)寫入LOG FILE,直到LGWR完成后,LGWR才可以被再次觸發(fā),在LGWR觸發(fā)到完成期間所有對數(shù)據(jù)庫的操作仍然可以不間斷的加入LOG BUFFER。在這段時間內(nèi),LGWR不再接收其它條件的觸發(fā),比如緊跟前一個COMMIT之后的其它COMMIT(復(fù)數(shù))都要等待LGWR完成后才可以再次觸發(fā)LGWR,并在LGWR下次被觸發(fā)時,將積累的REDO BUFFER條目一次性寫入REDO LOG,后繼的COMMIT不會單個單個的觸發(fā)LGWR。
如果要查找的數(shù)據(jù)已緩存,則根據(jù)用戶的SQL操作類型決定如何操作,如果是SELECT則查看Oracle DB BUFFER塊的頭部是否有事務(wù),如果有,將利用回滾段進(jìn)行重構(gòu)出一致性塊再讀取,如果沒有則比較SELECT的SCN與Oracle DB BUFFER塊頭部的SCN如果比自己大,仍然同上,如果比自己小則認(rèn)這是一個非臟緩存,可以直接從這個Oracle DB BUFFER塊中讀取。如果是UPDATE則即使在Oracle DB BUFFER中找到一個沒有事務(wù),而且SCN比自己小的非臟緩存數(shù)據(jù)塊,服務(wù)器進(jìn)程仍然要到表的頭部對這條記錄申請加鎖,加鎖成功則進(jìn)行后續(xù)動作,如果不成功,則要等待前面的進(jìn)程解鎖后才能進(jìn)行動作。
【編輯推薦】