漫談PostgreSQL的日志實現(xiàn)機制 |
發(fā)布時間: 2012/7/26 16:57:44 |
1、事務的概念 事務是從實際生活中引入數(shù)據(jù)庫的一個概念,即事務內(nèi)的操作,要么全做,要么全不做。就像銀行轉賬一樣,當從一個帳戶轉出一部分錢之后,就必須在另一個帳戶中存入相同數(shù)目的錢,若是轉出錢之后,事務中止了,沒有在另一個帳戶中存錢,那么錢就不翼而飛了,這就是事務的原子性。當事務完成后,必須將其結果記錄下來,不然就無從知道事務是已經(jīng)發(fā)生還是尚未發(fā)生,這是事務的持久性。此外,事務還有隔離性和一致性。 2、為什么要引入日志? 首先,我們了解一下在數(shù)據(jù)庫中是如何實現(xiàn)一個事務的。當事務開始后,我們從磁盤中讀取數(shù)據(jù),然后對這些數(shù)據(jù)進行操作,可能是篩選、統(tǒng)計、更新等,還可以有一些新建數(shù)據(jù),總之,若發(fā)生數(shù)據(jù)變化后,當數(shù)據(jù)完成后,必須將這些變化后的數(shù)據(jù)重新寫入到磁盤中,這樣我們就完成了一個事務。當然這是最簡單的一個描述,下面我們來針對每個環(huán)節(jié)進行深入的分析。首先是從磁盤中讀取數(shù)據(jù),根據(jù)常識,我們知道,在一個應用系統(tǒng)中,我們可能經(jīng)常會讀取相同的數(shù)據(jù),如果每次都從磁盤讀取,因為磁盤IO比較慢,所以效率不高,性能不好。大家都能想得到,可以采用緩沖區(qū)機制來提高數(shù)據(jù)讀取的性能。本文主要目的不是緩沖區(qū)就不多說了。接下來是對數(shù)據(jù)的操作,事務完成后,我們需要把更新后的數(shù)據(jù)寫入到磁盤,這里又有同樣的問題出現(xiàn),磁盤IO的性能問題,那么有人說我們還可以用緩沖區(qū)機制啊?說的太好了,緩沖區(qū)確實幫我們緩解了磁盤IO性能的問題。但緩沖區(qū)機制在幫我們解決了磁盤IO性能問題的同時,又帶來了一個新的問題,如果發(fā)生了故障怎么辦?如果數(shù)據(jù)庫系統(tǒng)能千秋萬載永世長存的話就沒問題了,但現(xiàn)實是不可能的,如果系統(tǒng)發(fā)生故障,比如斷電、死機什么的,緩沖區(qū)中的數(shù)據(jù)就會丟失,想想你剛中了500W元彩票,結果服務器down機了,你的彩票隨著緩沖區(qū)的消失隨風消逝了,你是不是很想把服務器給啃了?什么?你說你無所謂,一切都是浮云......別告訴我你是火星人...... 我們言歸正傳,在數(shù)據(jù)庫系統(tǒng)的設計中,數(shù)據(jù)的丟失是不可接受的,為了解決緩沖區(qū)數(shù)據(jù)寫入磁盤的性能問題,引入了日志。在操作數(shù)據(jù)之前,我們先將操作記入日志,然后再修改數(shù)據(jù),當然不修改數(shù)據(jù)的日志好象沒什么意義,這樣,即使系統(tǒng)down機導致緩沖區(qū)丟失,也不會把500W元彩票化為烏有了。我們可以通過讀取日志,重做丟失的數(shù)據(jù)的操作,就可以保證丟失的數(shù)據(jù)全部恢復。有人說,寫日志與寫緩沖區(qū)不是一樣要寫磁盤嗎?這位同學說的太對了,真的是一樣的,都要進行寫磁盤操作,只是有那么一點點細微的差別,寫日志是順序寫入磁盤,而緩沖區(qū)則是隨機寫入磁盤。雖然只是這一點點差別,但對性能的影響卻是巨大的,有興趣的同學可以自己去試試喲。此外日志的數(shù)據(jù)量也遠遠小于要寫入的緩沖區(qū)的數(shù)據(jù)量。 有些人提問了,為什么要先將操作記入日志,然后再執(zhí)行操作修改數(shù)據(jù)呢?這是因為若是先執(zhí)行操作,那么在隨后寫入日志之前若是系統(tǒng)down機,那么就會丟失此次操作,在數(shù)據(jù)庫系統(tǒng)中稱之為WAL(write ahead log)。 3、日志緩沖區(qū)的引入 為進一步提高性能,引入了日志緩沖區(qū),批量將日志寫入到磁盤,而不再是產(chǎn)生一條就寫一條,這樣又帶來一個問題,在日志緩沖區(qū)寫入磁盤之前有可能會導致日志丟失,從而導致數(shù)據(jù)丟失。如何解決這個問題呢?我們需要對日志的作用進一步分析,日志是為了重做丟失的操作,若一個事務未提交之前,那么這個事務已進行的操作實際上并不重要,即使丟失也沒有什么影響。就像銀行轉帳一樣,從一個賬戶已經(jīng)轉出,此時系統(tǒng)故障,無法對另一個帳戶轉入,此事務會回滾,即系統(tǒng)會退回到帳戶轉出之前的狀態(tài),賬戶轉出操作無效,即使賬戶轉出的操作這條日志未被寫入磁盤導致操作丟失,當我們恢復時,并不會有什么影響,可能還加速了恢復的過程,少處理了一條日志。因此日志緩沖區(qū)的磁盤寫入時機可以被推遲,最晚不能晚于事務提交。實際上在日志緩沖區(qū)實現(xiàn)上還有一些其它的限制,如checkpoint、日志緩沖區(qū)已滿等,不一定要等到事務提交時才寫入磁盤。 4、lsn的由來和作用 既然已經(jīng)有了日志,就要發(fā)揮它的作用,在恢復過程中,通過讀取日志來重做操作,按什么順序來重做日志呢?記錄歷史操作的順序,是非常重要的,如果操作順序發(fā)現(xiàn)混亂,導致的后果也是非常嚴重的。比如對一個數(shù)值100先減去100,再翻倍,若是發(fā)生操作順序逆轉,先翻倍再減去100,得到的結果就大相徑庭了。這里就需要一個規(guī)則,給日志編個序號,我們按日志產(chǎn)生的順序給每條日志編號,然后按日志編號來重做日志,就不會發(fā)生日志重做發(fā)生混亂的情況。在實現(xiàn)的過程中,我們在記錄日志的時候,是按日志產(chǎn)生的順序依次寫入磁盤的,即使是寫到日志緩沖區(qū)中,也是按產(chǎn)生的順序依次寫到日志緩沖區(qū),再將日志緩沖區(qū)順序寫到磁盤中。因此我們可以采用日志在日志文件中的偏移來代替這個日志編號,不僅不需要額外的磁盤開銷,而且還能通過這個偏移迅速定位到這個日志,真是個神奇的想法,我們給這樣的日志編號起了一個特殊的名字:lsn,這就是lsn的由來。 但我們又發(fā)現(xiàn)一個新的問題,雖然我們知道了所有的歷史操作和它們之間的順序關系,但不知道這些操作的影響是否已經(jīng)保存到磁盤,如果簡單的重做所有操作,會不會把已經(jīng)做過的操作重復進行。比如購物轉賬轉了兩次錢出去?所以在每個數(shù)據(jù)塊的塊頭記錄下最后一次修改這個數(shù)據(jù)塊的操作的日志編號lsn,當重做日志時,數(shù)據(jù)塊加載到緩沖區(qū)中,稱之為頁面,若頁面的header中l(wèi)sn比當前重做日志的lsn小,則說明當前日志尚未被重做; 若不比當前重做日志的lsn小,即大于或等于當前重做日志的lsn,則說明當前日志已經(jīng)被重做,或不需要重做;通過這種方法,可以避免日志被重復重做,從而得到正確的恢復結果。 5、利用checkpoint加速恢復的過程 當系統(tǒng)發(fā)生故障后,由于有日志的存在我們不用擔心數(shù)據(jù)丟失,可以通過讀取日志來恢復,但若是系統(tǒng)已經(jīng)運行了很長時間,操作很多,日志很大的情況下,在進行日志恢復時恢復進程會十分慢長。在生產(chǎn)環(huán)境下,要求恢復的時間越短越好,怎么才能縮短恢復的時間呢?checkpoint就是解決這個問題的辦法。在日志中,引入一種特殊的日志類型,checkpoint日志,它表示在此之前的所有“臟數(shù)據(jù)”已經(jīng)寫入到磁盤,那么在它之前的日志在恢復過程中就可以忽略掉,而不用再處理。雖然我們希望checkpoint是一個瞬時的過程,但在實現(xiàn)上卻有很大的難度,我們不能瞬時將所有“臟數(shù)據(jù)”寫入磁盤,如果可以做到,也就不需要日志了。因此checkpoint是一個過程,有它的起始和結束,當checkpoint開始時,我們記錄當前日志的記錄偏移lsn,并標記所有的“臟數(shù)據(jù)”為準備寫入狀態(tài),接下來就是將具有準備寫入狀態(tài)的”臟數(shù)據(jù)”寫入磁盤,注意:在寫入的同時其它進程或線程有可能會產(chǎn)生新的“臟數(shù)據(jù)”,這些新產(chǎn)生的“臟數(shù)據(jù)”我們并不關心其是否寫入磁盤。當所有已標記的“臟數(shù)據(jù)”寫入磁盤之后,在日志中插入一條checkpoint日志,表示checkpoint已經(jīng)完成,同時它還記錄著checkpoint開始時的日志偏移,也稱為REDO偏移。當進行恢復時,首先找到最后一次checkpoint日志的位置,讀出checkpoint日志記錄,從中獲得REDO偏移,然后從REDO偏移開始恢復即可。通過調(diào)整checkpoint的間隔時間,可以得到一個可接受的故障恢復時間。 本文出自:億恩科技【www.czbl888.cn】 |