敲黑板:InnoDB的Double Write,你必須知道

前序

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後的髒頁刷新流程就是多了幾步操做:

  1. 在對緩衝池的髒頁進行刷新時,並不直接寫磁盤,而是會經過memcpy函數將髒頁先複製到內存中的Double write buffer

  2. 經過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中的頁寫入各個數據文件中,此時的寫入則是離散的

總結

  1. 當commit 一個修改語句時,若是redo log有空閒區域,直接寫redo log,若是redo log沒有空閒區域,那麼須要把被覆蓋的redo log對應的數據頁刷新到data file 中,最後改pool buffer中的記錄

  2. innodb的redo log 不會記錄完整的一頁數據,由於這樣日誌太大,它只會記錄那次(sequence)如何操做了(update,insert)哪頁(page)的哪行(row)

  3. 由於數據庫使用的頁(page,默認16KB)大小和操做系統對磁盤的操做頁(page,默認4KB)不同,當提交了一個頁須要刷新到磁盤,會有屢次IO, 此時刷了前面的8k時異常發生宕機。在系統恢復正常後,若是沒有double write機制,此時數據庫磁盤內的數據頁已損壞,沒法使用redo log進行恢復。

  4. 若是有double write buffer,會檢查double writer的數據的完整性,若是不完整直接丟棄double write buffer內容,從新執行那條redo log,若是double write buffer的數據是完整的,用double writer buffer的數據更新該數據頁,跳過該redo log。



往期推薦

優秀 !原來華爲是這樣使用 git rebase的

重要,知識點:InnoDB的插入緩衝

知識點:瞭解InnoDB的Checkpoint技術

知識點:瞭解InnoDB存儲引擎的內存池

知識點:瞭解InnoDB的後臺線程




本文分享自微信公衆號 - 架構技術專欄(jiagoujishu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索