說到數據庫事務,你們腦子裏必定很容易蹦出一堆事務的相關知識,如事務的ACID特性,隔離級別,解決的問題(髒讀,不可重複讀,幻讀)等等,可是可能不多有人真正的清楚事務的這些特性又是怎麼實現的,爲何要有四個隔離級別。html
在以前的文章咱們已經瞭解了MySQL中事務的隔離性的實現原理,今天就繼續來聊一聊MySQL持久性的實現原理。mysql
固然MySQL博大精深,文章疏漏之處在所不免,歡迎批評指正。git
說明github
MySQL的事務實現邏輯是位於引擎層的,而且不是全部的引擎都支持事務的,下面的說明都是以InnoDB引擎爲基準。sql
在往下學習以前,咱們須要先來了解下InnoDB是怎麼來讀寫數據的。咱們知道數據庫的數據都是存放在磁盤中的,而後咱們也知道磁盤I/O的成本是很大的,若是每次讀寫數據都要訪問磁盤,數據庫的效率就會很是低。爲了解決這個問題,InnoDB提供了 Buffer Pool 做爲訪問數據庫數據的緩衝。數據庫
Buffer Pool 是位於內存的,包含了磁盤中部分數據頁的映射。當須要讀取數據時,InnoDB會首先嚐試從Buffer Pool中讀取,讀取不到的話就會從磁盤讀取後放入Buffer Pool;當寫入數據時,會先寫入Buffer Pool的頁面,並把這樣的頁面標記爲dirty,並放到專門的flush list上,這些修改的數據頁會在後續某個時刻被刷新到磁盤中(這一過程稱爲刷髒,由其餘後臺線程負責) 。以下圖所示:segmentfault
這樣設計的好處是能夠把大量的磁盤I/O轉成內存讀寫,而且把對一個頁面的屢次修改merge成一次I/O操做(刷髒一次刷入整個頁面),避免每次讀寫操做都訪問磁盤,從而大大提高了數據庫的性能。app
持久性是指事務一旦提交,它對數據庫的改變就應該是永久性的,接下來的其餘操做或故障不該該對本次事務的修改有任何影響。ide
經過前面的介紹,咱們知道InnoDB使用 Buffer Pool 來提升讀寫的性能。可是 Buffer Pool 是在內存的,是易失性的,若是一個事務提交了事務後,MySQL忽然宕機,且此時Buffer Pool中修改的數據尚未刷新到磁盤中的話,就會致使數據的丟失,事務的持久性就沒法保證。函數
爲了解決這個問題,InnoDB引入了 redo log來實現數據修改的持久化。當數據修改時,InnoDB除了修改Buffer Pool中的數據,還會在redo log 記錄此次操做,並保證redo log早於對應的頁面落盤(通常在事務提交的時候),也就是常說的WAL。若MySQL忽然宕機了且尚未把數據刷回磁盤,重啓後,MySQL會經過已經寫入磁盤的redo log來恢復沒有被刷新到磁盤的數據頁。
爲了提升性能,和數據頁相似,redo log 也包括兩部分:一是內存中的日誌緩衝(redo log buffer),該部分日誌是易失性的;二是磁盤上的重作日誌文件(redo log file),該部分日誌是持久的。redo log是物理日誌,記錄的是數據庫中物理頁的狀況 。
當數據發生修改時,InnoDB不只會修改Buffer Pool中的數據,也會在redo log buffer記錄此次操做;當事務提交時,會對redo log buffer進行刷盤,記錄到redo log file中。若是MySQL宕機,重啓時能夠讀取redo log file中的數據,對數據庫進行恢復。這樣就不須要每次提交事務都實時進行刷髒了。
注意點:
事務提交的時候,寫入redo log 相比於直接刷髒的好處主要有三點:
一次DML可能涉及到數據的修改和redo log的記錄,那它們的執行順序是怎麼樣的呢?網上的文章有的說先修改數據,後記錄redo log,有的說先記錄redo log,後改數據,那真實的狀況是如何呢?
首先經過上面的說明咱們知道,redo log buffer在事務提交的時候就會寫入redo log file的,而刷髒則是在後續的某個時刻,因此能夠肯定的是先記錄redo log,後修改data page(WAL固然是日誌先寫啦)。
那接下來的問題就是先寫redo log buffer仍是先修改Buffer Pool了。要了解這個問題,咱們先要了解InnoDB中,一次DML的執行過程是怎麼樣的。一次DML的執行過程涉及了數據的修改,加鎖,解鎖,redo log的記錄和undo log的記錄等,也是須要保證原子性的,而InnoDB經過MTR(Mini-transactions)來保證一次DML操做的原子性。
首先來看MTR的定義:
An internal phase of
InnoDB
processing, when making changes at the
physical level to internal data structures during
DML operations. A Mini-transactions (mtr) has no notion of
rollback; multiple Mini-transactionss can occur within a single
transaction. Mini-transactionss write information to the
redo log that is used during
crash recovery. A Mini-transactions can also happen outside the context of a regular transaction, for example during
purge processing by background threads.
MTR 是一個短原子操做,不能回滾,由於它自己就是原子的。數據頁的變動必須經過MTR,MTR 會把DML操做對數據頁的修改記錄到 redo log裏。
下面來簡單看下MTR的過程:
因而可知,InnoDB是先修改Buffer Pool,後寫redo log buffer的。
在任何狀況下,InnoDB啓動時都會嘗試執行recovery操做。在恢復過程當中,須要redo log參與,而若是還開啓了binlog,那就還須要binlog、undo log的參與。由於有可能數據已經寫入binlog了,可是redo log尚未刷盤的時候數據庫就奔潰了(事務是InnoDB引擎的特性,修改了數據不必定提交了,而binlog是MySQL服務層的特性,修改數據就會記錄了),這時候就須要redo log,binlog和undo log三者的參與來判斷是否有還沒提交的事務,未提交的事務進行回滾或者提交操做。
下面來簡單說下僅利用redo log恢復數據的過程:
Recover過程:故障恢復包含三個階段:Analysis,Redo和Undo。Analysis階段的任務主要是利用Checkpoint及Log中的信息確認後續Redo和Undo階段的操做範圍,經過Log修正Checkpoint中記錄的Dirty Page集合信息,並用其中涉及最小的LSN位置做爲下一步Redo的開始位置RedoLSN。同時修正Checkpoint中記錄的活躍事務集合(未提交事務),做爲Undo過程的回滾對象;Redo階段從Analysis得到的RedoLSN出發,重放全部的Log中的Redo內容,注意這裏也包含了未Commit事務;最後Undo階段對全部未提交事務利用Undo信息進行回滾,經過Log的PrevLSN能夠順序找到事務全部須要回滾的修改。
什麼是LSN?
LSN也就是log sequence number,也日誌的序列號,是一個單調遞增的64位無符號整數。redo log和數據頁都保存着LSN,能夠用做數據恢復的依據。LSN更大的表示所引用的日誌記錄所描述的變化發生在更後面。
什麼是Checkpoint?
Checkpoint表示一個保存點,在這個點以前的數據頁的修改(log LSN<Checkpoint LSN)都已經寫入磁盤文件了。InnoDB每次刷盤以後都會記錄Checkpoint,把最新的redo log LSN 記錄到Checkpoint LSN 裏,方便恢復數據的時候做爲起始點的判斷。
轉載請註明做者和文章出處
做者: X先生
http://www.javashuo.com/article/p-wklrlpzq-nu.html
以爲不錯的話請幫忙收藏點贊~