前言
數據庫系統(tǒng)與文件系統(tǒng)最大的區(qū)別在于數據庫能保證操作的原子性,一個操作要么不做要么都做,即使在數據庫宕機的情況下,也不會出現(xiàn)操作一半的情況,這個就需要數據庫的日志和一套完善的崩潰恢復機制來保證。本文仔細剖析了InnoDB的崩潰恢復流程,代碼基于5.6分支。
基礎知識
lsn: 可以理解為數據庫從創(chuàng)建以來產生的redo日志量,這個值越大,說明數據庫的更新越多,也可以理解為更新的時刻。此外,每個數據頁上也有一個lsn,表示最后被修改時的lsn,值越大表示越晚被修改。比如,數據頁A的lsn為100,數據頁B的lsn為200,checkpoint lsn為150,系統(tǒng)lsn為300,表示當前系統(tǒng)已經更新到300,小于150的數據頁已經被刷到磁盤上,因此數據頁A的最新數據一定在磁盤上,而數據頁B則不一定,有可能還在內存中。
redo日志: 現(xiàn)代數據庫都需要寫redo日志,例如修改一條數據,首先寫redo日志,然后再寫數據。在寫完redo日志后,就直接給客戶端返回成功。這樣雖然看過去多寫了一次盤,但是由于把對磁盤的隨機寫入(寫數據)轉換成了順序的寫入(寫redo日志),性能有很大幅度的提高。當數據庫掛了之后,通過掃描redo日志,就能找出那些沒有刷盤的數據頁(在崩潰之前可能數據頁僅僅在內存中修改了,但是還沒來得及寫盤),保證數據不丟。
undo日志: 數據庫還提供類似撤銷的功能,當你發(fā)現(xiàn)修改錯一些數據時,可以使用rollback指令回滾之前的操作。這個功能需要undo日志來支持。此外,現(xiàn)代的關系型數據庫為了提高并發(fā)(同一條記錄,不同線程的讀取不沖突,讀寫和寫讀不沖突,只有同時寫才沖突),都實現(xiàn)了類似MVCC的機制,在InnoDB中,這個也依賴undo日志。為了實現(xiàn)統(tǒng)一的管理,與redo日志不同,undo日志在Buffer Pool中有對應的數據頁,與普通的數據頁一起管理,依據LRU規(guī)則也會被淘汰出內存,后續(xù)再從磁盤讀取。與普通的數據頁一樣,對undo頁的修改,也需要先寫redo日志。
檢查點: 英文名為checkpoint。數據庫為了提高性能,數據頁在內存修改后并不是每次都會刷到磁盤上。checkpoint之前的數據頁保證一定落盤了,這樣之前的日志就沒有用了(由于InnoDB redolog日志循環(huán)使用,這時這部分日志就可以被覆蓋),checkpoint之后的數據頁有可能落盤,也有可能沒有落盤,所以checkpoint之后的日志在崩潰恢復的時候還是需要被使用的。InnoDB會依據臟頁的刷新情況,定期推進checkpoint,從而減少數據庫崩潰恢復的時間。檢查點的信息在第一個日志文件的頭部。
崩潰恢復: 用戶修改了數據,并且收到了成功的消息,然而對數據庫來說,可能這個時候修改后的數據還沒有落盤,如果這時候數據庫掛了,重啟后,數據庫需要從日志中把這些修改后的數據給撈出來,重新寫入磁盤,保證用戶的數據不丟。這個從日志中撈數據的過程就是崩潰恢復的主要任務,也可以成為數據庫前滾。當然,在崩潰恢復中還需要回滾沒有提交的事務,提交沒有提交成功的事務。由于回滾操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志來保證,所以崩潰恢復先做redo前滾,然后做undo回滾。
我們從源碼角度仔細剖析一下數據庫崩潰恢復過程。整個過程都在引擎初始化階段完成(
延伸閱讀
學習是年輕人改變自己的最好方式