標籤:事務 簡單的 帳戶 檔案 統計 篩選 資料庫 載入 設計
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的日誌實現機制