mysql源碼:關於innodb中兩次寫的探索

兩次寫能夠說是在Innodb中很獨特的一個功能點,而關於它的說明或者解釋很是少,至於它存在的緣由更沒有多少文章來講,因此我打算專門對它作一次說明。數據庫

首先說明一下爲何會有兩次寫這個東西:
由於innodb中的日誌是邏輯的,所謂邏輯就是好比當插入一條記錄時,它可能會致使在某一個頁面(這條記錄最終被插入的位置)的多個偏移位置寫入某個長度的值,好比頁頭的記錄數,槽數,頁尾槽數據,頁中的記錄值等等,這些本是一些物理操做,而innodb爲了節約日誌量及其它一些緣由,設計爲邏輯處理的方式,那就是它會在一個頁面的基礎上,把一條記錄插入,那麼在日誌記錄中記錄的內容爲表空間號、頁面號、記錄的各個列的值等等,在內部轉換爲上面的物理操做。數組

但這裏的一個問題是,若是那個頁面自己是錯誤的,這種錯誤有多是由於寫斷裂(1個頁面爲16K,分屢次寫入,後面的有可能沒有寫成功,致使這個頁面不完整)引發的,那麼這個邏輯操做就沒辦法完成了,由於它的前提是這個頁面仍是正確的,完整的,由於若是這個頁面不正確的話,這個頁面裏的數據是無效的,有可能產生各類不可預料的問題。緩存

那麼正是由於這個問題,因此必需要首先保證這個頁面是正確的,方法就是兩次寫,它的思想最終是一種備份思想,也就是一種鏡像。性能

下面就它的實現方式說明一下:測試

首先是它的建立,在初始化一個庫的時候,系統會在系統頁面5號頁面的尾部(大約是16K-200字節的位置)初始化全部關於兩次寫的信息,這些信息包括:spa

----------------------------------------------------------------
#define TRX_SYS_DOUBLEWRITE_FSEG 0 /*這裏存儲的是兩次寫頁面所在的段的地址信息 */
#define TRX_SYS_DOUBLEWRITE_MAGIC FSEG_HEADER_SIZE
/*!< 用來判斷是否是已經初始化過兩次寫頁面 */
#define TRX_SYS_DOUBLEWRITE_BLOCK1 (4 + FSEG_HEADER_SIZE)
/*!兩次寫頁面的第一個簇的首地址,兩次寫頁面總共有2個簇,一個簇爲64個頁面*/
#define TRX_SYS_DOUBLEWRITE_BLOCK2 (8 + FSEG_HEADER_SIZE)
/*!第二個簇的首地址*/
#define TRX_SYS_DOUBLEWRITE_REPEAT 12 /*!< 將上面的MAGCI、BLOCK1與BLOCK2重複存儲,防止本身的不完整*/
----------------------------------------------------------------設計

兩次寫總共包括2M(默認值)的數據,有2個BLOCK,那麼每個BLOCK是1M,每一個頁面是16K,那麼一個BLOCK包括64個頁面,正是一個簇的大小。,因此其實兩次寫頁面的空間是2個簇的空間。日誌

那麼初始化所要作的工做就是將上面的信息補充完整,BLOCK1與BLOCK2分別對應2個簇的首地址,同時還要申請2個簇的內存空間,用來緩存這些數據。事務

除上面的信息以外,還會有一個空間用來存儲這128個頁面的頁面信息,是用來在刷兩次寫頁面以後,要作對應的頁面刷盤操做,這是一個長度爲128的數組。內存

有了上面的信息以後,則兩次寫初始化完成。

下面說明一下它的使用過程:
在作頁面刷盤的時候,若是開啓了兩次寫的功能,則innodb要作的不是簡單的直接將數據作io操做寫入到硬盤,而是先將當前要寫的頁面按照順序拷貝到兩次寫內存緩存空間中去,上面已經說了它的大小爲128個頁面的大小,同時要在頁面數組中對應的將頁面的地址記錄下來,而後就算刷盤完成了,但實際上,此時要寫出的頁面都在兩次寫的內存緩存空間中的。

當緩存空間滿了的時候,上面的操做會觸發真正的刷盤操做,兩次寫的寫入是,首先判斷當前緩存中有多少個簇,也就是說判斷BLOCK1中有沒有數據,若是沒有數據則直接不寫了,若是有則寫入,以這個簇實際大小寫入,而後再判斷BLOCK2中有沒有數據,而後作一樣的處理。
在寫完兩次寫緩存中的數據以後,而後再將頁面數組中的每個頁面按照順序從前到後再一個一個的將其刷入到文件中,此時,才真正的將這些頁面刷盤完成。

固然兩次寫緩存寫出硬盤不僅是上面一個機會,其它刷盤的操做也會觸發這個操做,那時可能緩存中的頁面數尚未達到128個。

上面已經說完了兩次寫的寫入及初始化,最後說一下它是如何起做用的:

在數據庫啓動時(異常關閉的狀況下),都會作數據庫恢復(redo)操做,恢復的過程當中,數據庫都會檢查頁面是否是合法(校驗等等),若是發現一個頁面校驗結果不一致,則此時會用到兩次寫這個功能,這個特色也正是爲了處理這樣的錯誤而設計的。

此時的操做很明白了,將兩次寫的2個BLOCK(簇)都讀出來,而後將全部這些頁面寫回到對應的頁面中去,那麼這時能夠保證這些頁面是正確的,而且是在寫入前已經更新過的(最新數據)。
在寫回對應頁面中去以後,那麼就能夠在這基礎上繼續作數據庫恢復了,以後則不會再遇到這樣的問題了,由於已經將最後有可能產生寫斷裂的數據頁面都恢復了。

問題:
上面說的都是數據頁面有問題的狀況下能夠經過兩次寫頁面來恢復,可是若是2次寫頁面自己發生寫斷裂怎麼辦?
對於這個問題,實際上是不用擔憂的,由於若是兩次寫有問題,則自己數據頁面就沒有作寫操做,此時系統掛了,發生錯誤的是兩次寫頁面,而數據頁面在掛以前都是在buffer裏面,文件中仍是當前事務操做前的值,它本身沒有變,仍是一致狀態,因此兩次寫頁面壓根就不會被使用到。

總結:

1. 兩次寫在任什麼時候候記錄的都是數據庫最後發生改變的若干頁面(最多128個),在數據庫不斷工做的過程當中,它會不斷的被覆蓋,它始終是最新的數據,記錄的是修改以後的頁面數據,而不是修改以前的數據,它的做用不是還原數據,而是保證不會丟失修改。 2. 至於性能問題,表面看上去,它是每個頁面都寫了2遍,則會很是影響性能,但實際上,因爲將所寫的頁面都先緩存到內存中,到達128個以後才真正寫入,那麼對於磁盤而言,連續寫與分散寫(每一個頁面本身寫)的性能相差很大的,而兩次寫正是將一個簇數量的頁面組合起來造成2個連續的空間寫入到兩次寫空間中,有效的利用這了這特色,因此性能是不會相差1倍的。實際上通過測試,可能兩次寫使得性能下降了10%。 3. 有其它一些數據庫徹底沒有相似2次寫的問題,好比達夢等,這個主要是因爲它們採用了全物理的REDO,將一個頁面的寫操做都拆成一個個的小的物理寫入,這種狀況下就不會存在寫斷裂的狀況,由於無論怎麼寫,日誌都是對一個頁面操做的重演,在REDO作完以後,頁面的狀態確定是正確的。

相關文章
相關標籤/搜索