MySQL 持久化保障機制-redo 日志


我們在 聊一聊 MySQL 中的事務及其實現原理 中提到了 redo 日志,redo 日志是用來保證 MySQL 持久化功能的,需要注意的是 redo 日志是 InnoDB 引擎特有的功能。

為什么 InnoDB 引擎會引入 redo 日志作為中間層來保證 MySQL 持久化,而不是直接持久化到磁盤?我們先來看看《MySQL實戰45講》中提到的一個故事。

在《孔乙己》這篇文章,酒店掌櫃有一個粉板,專門用來記錄客人的賒賬記錄。如果賒賬的人不多,那么他可以把顧客名和賬目寫在板上。但如果賒賬的人多了,粉板總會有記不下的時候,這個時候掌櫃一定還有一個專門記錄賒賬的賬本。

如果有人要賒賬或者還賬的話,掌櫃一般有兩種做法:

  • 一種做法是直接把賬本翻出來,把這次賒的賬加上去或者扣除掉;
  • 另一種做法是先在粉板上記下這次的賬,等打烊以后再把賬本翻出來核算。

在生意紅火櫃台很忙時,掌櫃一定會選擇后者,因為前者操作實在是太麻煩了。首先,你得找到這個人的賒賬總額那條記錄。你想想,密密麻麻幾十頁,掌櫃要找到那個名字,可能還得帶上老花鏡慢慢找,找到之后再拿出算盤計算,最后再將結果寫回到賬本上。

這整個過程想想都麻煩。相比之下,還是先在粉板上記一下方便。你想想,如果掌櫃沒有粉板的幫助,每次記賬都得翻賬本,效率是不是低得讓人難以忍受?

同樣,在 MySQL 里也有這個問題,磁盤就相對於賬本,如果每一次的更新操作都需要寫進磁盤,然后磁盤也要找到對應的那條記錄,然后再更新,整個過程 IO 成本、查找成本都很高

為了解決這個問題,MySQL 的設計者就用了類似酒店掌櫃粉板的思路來提升更新效率,redo 日志跟酒店粉板一樣,用來臨時存儲,承擔一個中轉的角色

具體來說,當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log(粉板)里面,並更新內存,這個時候更新就算完成了。同時,InnoDB 引擎會在適當的時候,將這個操作記錄更新到磁盤里面,而這個更新往往是在系統比較空閑的時候做,這就像打烊以后掌櫃做的事

通過上面的這個故事你可以理解為什么需要引入 redo 日志,對 redo 日志有一定的了解,下面我們就來正式介紹 redo 日志,先從 redo 日志的結構開始:

redo 日志通用結構

上面是 redo 日志的通用結構,redo 日志記錄的是每個頁面(page)更改物理情況,所以 redo 日志整體來說是比較小的,存儲的信息不多,簡單的介紹一下這幾個字段的意思:

  • type:該條redo日志的類型。
  • space ID:表空間ID。
  • page number:頁號。
  • data:該條redo日志的具體內容。

redo 日志並非這么簡單,它非常的復雜,但是我們不需要對它庖丁解牛,因為它確實對我們來說沒啥用,我們只要記住 redo 日志會把事務在執行過程中對數據庫所做的所有修改都記錄下來,在之后系統崩潰重啟后可以把事務所做的任何修改都恢復出來

在事務提交時將所有修改過的內存中的頁面刷新到磁盤中相比,只將該事務執行過程中產生的 redo 日志刷新到磁盤的好處如下:

  • redo日志占用的空間非常小:存儲表空間ID、頁號、偏移量以及需要更新的值所需的存儲空間是很小的
  • redo日志是順序寫入磁盤的:在執行事務的過程中,每執行一條語句,就可能產生若干條redo日志,這些日志是按照產生的順序寫入磁盤的,也就是使用順序IO。

redo 日志工作原理

redo 日志是循環寫入的,因為 InnoDB 的 redo log 是固定大小的,比如可以配置為一組 4 個文件,每個文件的大小是 1GB,那么這塊“粉板”總共就可以記錄 4GB 的操作。從頭開始寫,寫到末尾就又回到開頭循環寫,如下面這個圖所示:

redo 日志

write pos 是當前記錄的位置,一邊寫一邊后移,寫到第 3 號文件末尾后就回到 0 號文件開頭。checkpoint 是當前要擦除的位置,也是往后推移並且循環的,擦除記錄前要把記錄更新到數據文件。

write pos 和 checkpoint 之間的是“粉板”上還空着的部分,可以用來記錄新的操作。如果 write pos 追上 checkpoint,表示“粉板”滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把 checkpoint 推進一下。

這大概就是 redo 日志的工作原理,你就把它想象成一塊黑板就好了。

redo日志緩沖區

redo 日志並不是直接寫入磁盤的,而是先寫入到緩存區,我們把這個緩沖區叫做 redo日志緩沖區。在服務器啟動時就向操作系統申請了一大片稱之為 redo log buffer 的連續內存空間,我們也可以簡稱為log buffer。這片內存空間被划分成若干個連續的 redo log block,如下圖所示:

redo日志緩沖區

在 MySQL Server 5.7 下 redo日志緩沖區的大小默認為 1M,我們可以通過 innodb_log_buffer_size 參數來設置 redo 日志緩沖區的大小。

向 log buffer 中寫入 redo 日志的過程是順序的,也就是先往前邊的 block中寫,當該 block 的空閑空間用完之后再往下一個 block 中寫。

先寫入緩沖區再寫磁盤,就會碰到一個問題,這個問題在 redis AOF 持久化方式時也遇到過,就是緩沖區和磁盤之間的數據如何同步

在 MySQL 的配置文件中提供了 innodb_flush_log_at_trx_commit 參數,這個可以用來控制緩沖區和磁盤之間的數據如何同步,這里有 0、1、2 三個選項,在我裝的 MySQL 下默認的是 1,簡單介紹一下這三個選項的區別:

  • 0:表示當提交事務時,並不將緩沖區的 redo 日志寫入磁盤的日志文件,而是等待主線程每秒刷新。
  • 1:在事務提交時將緩沖區的 redo 日志同步寫入到磁盤,保證一定會寫入成功。
  • 2:在事務提交時將緩沖區的 redo 日志異步寫入到磁盤,即不能完全保證 commit 時肯定會寫入 redo 日志文件,只是有這個動作。

我們使用默認值 1 就好,這樣可以保證 MySQL 異常重啟之后數據不丟失。

總結一下 redo 日志是 InnoDB 引擎特有的,有了 redo 日志 之后,InnoDB 就可以保證即使數據庫發生異常重啟,之前提交的記錄都不會丟失。

這篇文章從為什么要引入 redo 日志、redo 日志的結構和 redo 日志的寫入方式三個方面簡單聊了一下 MySQL 持久化保障機制 redo 日志,這東西可能工作沒啥用,面試時候可能用的上,希望這篇文章對你的學習或者工作有所幫助,感謝您的閱讀,如果您覺得文章有收獲,歡迎點個贊和轉發,謝謝。

最后

目前互聯網上很多大佬都有 MySQL 相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。

平頭哥的技術博文


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM