DBWn,CKPT,LGWR程式

路途中的人2012發表於2015-09-15

4.DBWn,CKPT,LGWR程式

   
首先,簡要介紹一下:
 
      DBWn
:資料庫塊寫入器(Database Block Writer)負責將髒塊寫入磁碟的後臺程式。
 
      CKPT
:檢查點程式(Checkpoint Process)只是更新資料檔案的檔案首部,以輔助建立檢查點的程式(DBWn)。
 
      LGWR
:日誌寫入器(Log Writer)負責將SGA中重做日誌緩衝區的內容重新整理輸出到磁碟。
 
     

      其實這三個程式都是為了更好地完成一件事:安全高效地實現記憶體資料塊寫入資料檔案,就是將記憶體中修改的資料反映到硬碟的資料檔案上。 

      將記憶體資料塊寫入資料檔案實在是一個相當複雜的過程,在這個過程中,首先要保證安全。所謂安全,就是在寫的過程中,一旦發生例項崩潰,要有一套完整的機制能夠保證使用者已經提交的資料不會丟失;其次,在保證安全的基礎上,要儘可能地提高效率。眾所周知,I/O操作是最昂貴的操作,所以應該儘可能地將髒資料塊收集到一定程度以後,再批次寫入磁碟中。 

      直觀上最簡單的解決方法就是,每當使用者提交的時候就將所改變的記憶體資料塊交給DBWn,由其寫入資料檔案。這樣的話,一定能夠保證提交的資料不會丟失。但是這種方式效率最為低下,在高併發環境中,一定會引起I/O方面的爭用。Oracle當然不會採用這種沒有伸縮性的方式。Oracle引入了CKPTLGWR這兩個後臺程式,這兩個程式與DBWn程式互相合作,提供了既安全又高效的寫髒資料塊的解決方法。

         第一:如何保證安全? 
     
使用者程式每次修改記憶體資料塊時,都會在日誌緩衝區(log buffer)中構造一個相應的重做條目(redo entry),該重做條目描述了被修改的資料塊在修改之前和修改之後的值。而LGWR程式則負責將這些重做條目寫入聯機日誌檔案。只要重做條目進入了聯機日誌檔案,那麼資料的安全就有保障了,否則這些資料都是有安全隱患的。LGWR是一個必須和前臺使用者程式通訊的程式。LGWR 承擔了維護系統資料完整性的任務,它保證了資料在任何情況下都不會丟失。

      假如DBWR在寫髒資料塊的過程中,突然發生例項崩潰時,該怎麼辦?我們已經知道,使用者提交時,Oracle是不一定會把提交的資料塊寫入資料檔案的。那麼例項崩潰時,必然會有一些已經提交但是還沒有被寫入資料檔案的記憶體資料塊丟失了。當例項再次啟動時,Oracle需要利用日誌檔案中記錄的重做條目在buffer cache中重新構造出被丟失的資料塊,從而完成前滾和回滾的工作,並將丟失的資料塊找回來。於是這裡就存在一個問題,就是Oracle在日誌檔案中找重做條目時,到底應該找哪些重做條目?換句話說,應該在日誌檔案中從哪個起點開始往後應用重做條目?注意,這裡所指的日誌檔案可能不止一個日誌檔案。

      這個起點意義重大,在日誌檔案中位於這個起點之前的重做條目所對應的在buffer cache中的髒資料塊已經被寫入了資料檔案,從而在例項崩潰以後的恢復中不需要去考慮。而這個起點以後的重做條目所對應的髒資料塊實際還沒有被寫入資料檔案,如果在例項崩潰以後的恢復中,需要從這個起點開始往後,依次取出日誌檔案中的重做條目進行恢復。考慮到目前的記憶體容量越來越大,buffer cache也越來越大,buffer cache中包含幾百萬個記憶體資料塊也是很正常的現象的前提下,如何才能最有效的來定位這個起點呢?

      為了能夠確定這個最佳的起點,Oracle引入了名為CKPT的後臺程式,通常也叫作檢查點程式(checkpoint process)。這個程式與DBWn共同合作,從而確定這個起點。同時,這個起點也有一個專門的名字,叫做檢查點位置(checkpoint position,該檢查點位置記錄在控制檔案裡)。Oracle為了在檢查點的演算法上更加的具有可擴充套件性(也就是為了能夠在巨大的buffer cache下依然有效工作),引入了檢查點佇列(checkpoint queue),該佇列上串起來的都是髒資料塊所對應的buffer header。而每次DBWn寫髒資料塊時,也是從檢查點佇列上掃描髒資料塊,並將這些髒資料塊實際寫入資料檔案的。當寫完以後,DBWn會將這些已經寫入資料檔案的髒資料塊從檢查點佇列上摘下來。這樣即便是在巨大的buffer cache下工作,CKPT也能夠快速的確定哪些髒資料塊已經被寫入了資料檔案,而哪些還沒有寫入資料檔案,顯然,只要在檢查點佇列上的資料塊都是還沒有寫入資料檔案的髒資料塊。同時為了能夠儘量減少例項崩潰後恢復的時間,Oracle還引入了增量檢查點(incremental checkpoint),從而增加了檢查點啟動的次數。如果每次檢查點啟動的間隔時間過長的話,再加上記憶體很大,可能會使得恢復的時間過長。因為前一次檢查點啟動以後,標識出了這個起點。然後在第二次檢查點啟動之前,DBWn可能已經將很多髒資料塊已經寫入了資料檔案,而假如在第二次檢查點啟動之前發生例項崩潰,導致在日誌檔案中,所標識的起點仍然是上一次檢查點啟動時所標識的,導致Oracle不知道這個起點以後的很多重做條目所對應的髒資料塊實際上已經寫入了資料檔案,從而使得Oracle在例項恢復時重複地處理一遍,效率低下,浪費時間。

      上面說到了有關CKPT的兩個重要的概念:檢查點佇列(包括檔案佇列)和增量檢查點。檢查點佇列上的buffer header是按照資料塊第一次被修改的時間的先後順序來排列的。越早修改的資料塊的buffer header排在越前面,同時如果一個資料塊被修改了多次的話,在該連結串列上也只出現一次。而且,檢查點佇列上的buffer header還記錄了髒資料塊在第一次被修改時,所對應的重做條目在重做日誌檔案中的地址,也就是LRBALow Redo Block Address),Low表示第一次修改時對應的RBA。每個檢查點都會由checkpoint queue latch來保護。
     
上面所描述的概念,用一句話來概括,其實就是DBWn負責寫檢查點佇列上的髒資料塊,而CKPT負責記錄當前檢查點佇列的第一個資料塊所對應的的重做條目在日誌檔案中的地址。而到底應該寫哪些髒資料塊,寫多少髒資料塊,則要到檢查點佇列上才能確定的。
     
下面是我個人的理解:假設前臺使用者在某一分鐘內正源源不斷地進行更新資料。他每更新一個記憶體資料塊,就會觸發兩個動作,其一,LGWR程式就會在日誌緩衝區構造一個重做條目,儲存一些修改的資訊,便於今後在記憶體中重新構造出這塊記憶體資料塊。並把這個重做條目寫入到重做日誌檔案。其二,CKPT程式負責將該記憶體塊對應的buffer header加入檢查點佇列,buffer header相當於一個指標,透過它能夠找到對應的記憶體塊資料!那麼DBWn程式在幹嗎呢,它在收集修改過的記憶體資料塊,假設收集到50塊的時候,就將這50塊修改過的記憶體資料反映到硬碟的資料檔案裡面。不過DBWn只負責收集,修改硬碟資料交給了作業系統去處理,處理成功之後,作業系統給DBWn 一個訊號,這個時候,DBWn就將這50塊記憶體資料塊對應的檢查點從檢查點佇列上面摘下來!這樣如果系統在寫硬碟資料的時候突然崩潰,檢查點佇列裡面對應的資料塊都是修改了,但沒來得及寫入硬碟的!這樣就保證了資料的安全與高效。不過這裡我有一個疑問,增量檢查點好像沒用啊?我的理解肯定有點偏差。希望大家前來討論!

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29067253/viewspace-1800616/,如需轉載,請註明出處,否則將追究法律責任。

相關文章