前序
InnoDB引擎有幾個重點特性,爲其帶來了更好的性能和可靠性:mysql
-
插入緩衝(Insert Buffer)git
-
兩次寫(Double Write)web
-
自適應哈希索引(Adaptive Hash Index)sql
-
異步IO(Async IO)數據庫
-
刷新鄰接頁(Flush Neighbor Page)微信
今天咱們的主題就是 兩次寫(Double Write)
, 先一句話歸納下:架構
上一次咱們講過Insert Buffer 是用來提升存儲引擎性能上的提高,Double Write 就是爲了在數據庫崩潰恢復時保證數據不丟失的一個重要特性,保證了數據的可靠性。koa
概念點
如圖,仍是先來講幾個基礎的概念:異步
-
數據庫表空間由段(segment)、區(extent)、頁(page)組成編輯器
-
默認狀況下有一個共享表空間ibdata1,如使用了innodb_file_per_table則每張表獨立表空間(指存放數據、索引、插入緩衝bitmap頁)
-
段包括了數據段(B+樹的葉子結點)、索引段、回滾段
-
區,由連續的頁組成,任何狀況下每一個區都爲1M,一個區中有64個連續頁(16k)
-
頁,數據頁(B-tree Node)默認大小爲16KB
-
文件系統一頁 默認大小爲4KB
-
盤片被分爲許多扇形的區域,每一個區域叫一個扇區,硬盤中每一個扇區的大小固定爲512字節
-
髒頁,當數據從磁盤加載到緩衝池的數據頁後,數據頁內容被修改後,此數據頁稱爲髒頁
出現的問題
經過上次講的 重要,知識點:InnoDB的插入緩衝 咱們知道,髒頁會在某些場景下進行刷盤,將緩衝池內的髒頁數據落地到磁盤。
由於存儲引擎緩衝池內的數據頁大小默認爲16KB,而文件系統一頁大小爲4KB,因此在進行刷盤操做時,就有可能發生以下場景:
如圖所示,數據庫準備刷新髒頁時,須要四次IO才能將16KB的數據頁刷入磁盤。
但當執行完第二次IO時,數據庫發生意外宕機,致使此時才刷了2個文件系統裏的頁,這種狀況被稱爲寫失效(partial page write)。
此時重啓後,磁盤上就是不完整的數據頁,就算使用redo log也是沒法進行恢復的。
注意:
-
redo log沒法恢復數據頁損壞的問題,恢復必須是數據頁正常而且redo log正常。
-
這裏要知道一點,redo log中記錄的是對頁的物理操做,如偏移量600,寫'xxxx'記錄。
-
若是這個頁自己已經發生了損壞,再對其進行重作是沒有意義的
該怎麼解決這個問題
那應該怎麼來解決這個問題呢?其實你們想一下就會有個大概的答案,就是給它搞個備份唄。
若是寫髒頁的時候發生宕機,在重啓後使用下備份先恢復下數據頁在寫磁盤就能夠了,其實這就是Double Write
。
Double Write 出現
千呼萬喚始出來,爲了防止咱們可憐的數據被破壞,InnoDB存儲引擎提供了重要的Double Write 特性,避免了數據丟失的慘劇發生。
下面咱們來慢慢的來看看Double Write 究竟是怎麼提升可靠性的
Double Write 解決的問題
在數據庫進行髒頁刷新時,若是此時宕機,有可能會致使磁盤數據頁損壞,丟失咱們重要的數據。此時就算重作日誌也是沒法進行恢復的,由於重作日誌記錄的是對頁的物理修改。
其實就是在重作日誌前,用戶須要一個頁的副本,當寫入失效發生時,先經過頁的副原本還原該頁,再進行重作,這就是double write。
Double Write 架構
如圖,其實Double Write 分爲了兩個組成部分:
-
內存中的double write buffer -
物理磁盤上共享表空間中連續的128個頁,即2個區(extent),大小一樣爲2MB
能夠看出,有了Double write後的髒頁刷新流程就是多了幾步操做:
-
在對緩衝池的髒頁進行刷新時,並不直接寫磁盤,而是會經過memcpy函數將髒頁先複製到內存中的Double write buffer
-
經過Double write buffer再分兩次,每次1MB順序地寫入共享表空間的物理磁盤上,而後立刻調用fsync函數,同步磁盤,避免緩衝寫帶來的問題
Double write崩潰恢復
如圖,若是操做系統在將頁寫入磁盤的過程當中發生了崩潰,在恢復過程當中,InnoDB存儲引擎能夠從共享表空間中的Double write中找到該頁的一個副本,將其複製到表空間文件,再應用重作日誌。
下面顯示了一個由Double write進行恢復的狀況:
090923 12:36:32 mysqld restarted
090923 12:26:33 InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Crash recovery may have faild for some .ibd files!
InnoDB: Restoring possible half-written data pages from the doublewrite.
InnoDB: buffer...
Double Write 的問題
Double write buffer 它是在物理文件上的一個buffer, 其實也就是file,因此它會致使系統有更多的fsync操做,而由於硬盤的fsync性能問題,因此也會影響到數據庫的總體性能。
Double write頁是連續的,所以這個過程是順序寫的,開銷並非很大。
在完成Double write頁的寫入後,再將Double write buffer中的頁寫入各個數據文件中,此時的寫入則是離散的
總結
-
當commit 一個修改語句時,若是redo log有空閒區域,直接寫redo log,若是redo log沒有空閒區域,那麼須要把被覆蓋的redo log對應的數據頁刷新到data file 中,最後改pool buffer中的記錄
-
innodb的redo log 不會記錄完整的一頁數據,由於這樣日誌太大,它只會記錄那次(sequence)如何操做了(update,insert)哪頁(page)的哪行(row)
-
由於數據庫使用的頁(page,默認16KB)大小和操做系統對磁盤的操做頁(page,默認4KB)不同,當提交了一個頁須要刷新到磁盤,會有屢次IO, 此時刷了前面的8k時異常發生宕機。在系統恢復正常後,若是沒有double write機制,此時數據庫磁盤內的數據頁已損壞,沒法使用redo log進行恢復。
-
若是有double write buffer,會檢查double writer的數據的完整性,若是不完整直接丟棄double write buffer內容,從新執行那條redo log,若是double write buffer的數據是完整的,用double writer buffer的數據更新該數據頁,跳過該redo log。
往期推薦
本文分享自微信公衆號 - 架構技術專欄(jiagoujishu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。