1. 程式人生 > >postgresql的日誌實現機制

postgresql的日誌實現機制

此外 一個 進行 比較 批量 原子 註意 機制 依次

1、事務的概念
事務是從實際生活中引入數據庫的一個概念,即事務內的操作,要麽全做,要麽全不做。就像銀行轉賬一樣,當從一個帳戶轉出一部分錢之後,就必須在另一個帳戶中存入相同數目的錢,若是轉出錢之後,事務中止了,沒有在另一個帳戶中存錢,那麽錢就不翼而飛了,這就是事務的原子性。當事務完成後,必須將其結果記錄下來,不然就無從知道事務是已經發生還是尚未發生,這是事務的持久性。此外,事務還有隔離性和一致性。


2、為什麽要引入日誌?
首先,我們了解一下在數據庫中是如何實現一個事務的。當事務開始後,我們從磁盤中讀取數據,然後對這些數據進行操作,可能是篩選、統計、更新等,還可以有一些新建數據,總之,若發生數據變化後,當數據完成後,必須將這些變化後的數據重新寫入到磁盤中,這樣我們就完成了一個事務。當然這是最簡單的一個描述,下面我們來針對每個環節進行深入的分析。首先是從磁盤中讀取數據,根據常識,我們知道,在一個應用系統中,我們可能經常會讀取相同的數據,如果每次都從磁盤讀取,因為磁盤IO比較慢,所以效率不高,性能不好。大家都能想得到,可以采用緩沖區機制來提高數據讀取的性能。本文主要目的不是緩沖區就不多說了。接下來是對數據的操作,事務完成後,我們需要把更新後的數據寫入到磁盤,這裏又有同樣的問題出現,磁盤IO的性能問題,那麽有人說我們還可以用緩沖區機制啊?說的太好了,緩沖區確實幫我們緩解了磁盤IO性能的問題。但緩沖區機制在幫我們解決了磁盤IO性能問題的同時,又帶來了一個新的問題,如果發生了故障怎麽辦?
在數據庫系統的設計中,數據的丟失是不可接受的,為了解決緩沖區數據寫入磁盤的性能問題,引入了日誌。在操作數據之前,我們先將操作記入日誌,然後再修改數據,當然不修改數據的日誌好象沒什麽意義。我們可以通過讀取日誌,重做丟失的數據的操作,就可以保證丟失的數據全部恢復。有人說,寫日誌與寫緩沖區不是一樣要寫磁盤嗎?這位同學說的太對了,真的是一樣的,都要進行寫磁盤操作,只是有那麽一點點細微的差別,寫日誌是順序寫入磁盤,而緩沖區則是隨機寫入磁盤。雖然只是這一點點差別,但對性能的影響卻是巨大的,有興趣的同學可以自己去試試喲。此外日誌的數據量也遠遠小於要寫入的緩沖區的數據量。
有些人提問了,為什麽要先將操作記入日誌,然後再執行操作修改數據呢?這是因為若是先執行操作,那麽在隨後寫入日誌之前若是系統down機,那麽就會丟失此次操作,在數據庫系統中稱之為WAL(write ahead log)。


3、日誌緩沖區的引入
為進一步提高性能,引入了日誌緩沖區,批量將日誌寫入到磁盤,而不再是產生一條就寫一條,這樣又帶來一個問題,在日誌緩沖區寫入磁盤之前有可能會導致日誌丟失,從而導致數據丟失。如何解決這個問題呢?我們需要對日誌的作用進一步分析,日誌是為了重做丟失的操作,若一個事務未提交之前,那麽這個事務已進行的操作實際上並不重要,即使丟失也沒有什麽影響。就像銀行轉帳一樣,從一個賬戶已經轉出,此時系統故障,無法對另一個帳戶轉入,此事務會回滾,即系統會退回到帳戶轉出之前的狀態,賬戶轉出操作無效,即使賬戶轉出的操作這條日誌未被寫入磁盤導致操作丟失,當我們恢復時,並不會有什麽影響,可能還加速了恢復的過程,少處理了一條日誌。因此日誌緩沖區的磁盤寫入時機可以被推遲,最晚不能晚於事務提交。實際上在日誌緩沖區實現上還有一些其它的限制,如checkpoint、日誌緩沖區已滿等,不一定要等到事務提交時才寫入磁盤。


4、lsn的由來和作用
既然已經有了日誌,就要發揮它的作用,在恢復過程中,通過讀取日誌來重做操作,按什麽順序來重做日誌呢?記錄歷史操作的順序,是非常重要的,如果操作順序發現混亂,導致的後果也是非常嚴重的。比如對一個數值100先減去100,再翻倍,若是發生操作順序逆轉,先翻倍再減去100,得到的結果就大相徑庭了。這裏就需要一個規則,給日誌編個序號,我們按日誌產生的順序給每條日誌編號,然後按日誌編號來重做日誌,就不會發生日誌重做發生混亂的情況。在實現的過程中,我們在記錄日誌的時候,是按日誌產生的順序依次寫入磁盤的,即使是寫到日誌緩沖區中,也是按產生的順序依次寫到日誌緩沖區,再將日誌緩沖區順序寫到磁盤中。因此我們可以采用日誌在日誌文件中的偏移來代替這個日誌編號,不僅不需要額外的磁盤開銷,而且還能通過這個偏移迅速定位到這個日誌,真是個神奇的想法,我們給這樣的日誌編號起了一個特殊的名字:lsn,這就是lsn的由來。
但我們又發現一個新的問題,雖然我們知道了所有的歷史操作和它們之間的順序關系,但不知道這些操作的影響是否已經保存到磁盤,如果簡單的重做所有操作,會不會把已經做過的操作重復進行。比如購物轉賬轉了兩次錢出去?所以在每個數據塊的塊頭記錄下最後一次修改這個數據塊的操作的日誌編號lsn,當重做日誌時,數據塊加載到緩沖區中,稱之為頁面,若頁面的header中lsn比當前重做日誌的lsn小,則說明當前日誌尚未被重做; 若不比當前重做日誌的lsn小,即大於或等於當前重做日誌的lsn,則說明當前日誌已經被重做,或不需要重做;通過這種方法,可以避免日誌被重復重做,從而得到正確的恢復結果。


5、利用checkpoint加速恢復的過程
當系統發生故障後,由於有日誌的存在我們不用擔心數據丟失,可以通過讀取日誌來恢復,但若是系統已經運行了很長時間,操作很多,日誌很大的情況下,在進行日誌恢復時恢復進程會十分慢長。在生產環境下,要求恢復的時間越短越好,怎麽才能縮短恢復的時間呢?checkpoint就是解決這個問題的辦法。在日誌中,引入一種特殊的日誌類型,checkpoint日誌,它表示在此之前的所有“臟數據”已經寫入到磁盤,那麽在它之前的日誌在恢復過程中就可以忽略掉,而不用再處理。雖然我們希望checkpoint是一個瞬時的過程,但在實現上卻有很大的難度,我們不能瞬時將所有“臟數據”寫入磁盤,如果可以做到,也就不需要日誌了。因此checkpoint是一個過程,有它的起始和結束,當checkpoint開始時,我們記錄當前日誌的記錄偏移lsn,並標記所有的“臟數據”為準備寫入狀態,接下來就是將具有準備寫入狀態的”臟數據”寫入磁盤,註意:在寫入的同時其它進程或線程有可能會產生新的“臟數據”,這些新產生的“臟數據”我們並不關心其是否寫入磁盤。當所有已標記的“臟數據”寫入磁盤之後,在日誌中插入一條checkpoint日誌,表示checkpoint已經完成,同時它還記錄著checkpoint開始時的日誌偏移,也稱為REDO偏移。當進行恢復時,首先找到最後一次checkpoint日誌的位置,讀出checkpoint日誌記錄,從中獲得REDO偏移,然後從REDO偏移開始恢復即可。通過調整checkpoint的間隔時間,可以得到一個可接受的故障恢復時間。

postgresql的日誌實現機制