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

  • 2020 年 2 月 24 日
  • 筆記

來源:https://www.cnblogs.com/jamaler/p/12174517.html

redo 日誌是用來保證 MySQL 持久化功能的,需要注意的是 redo 日誌是 InnoDB 引擎特有的功能。

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

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

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

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

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

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

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

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

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

通過上面的這個故事你可以理解為什麼需要引入 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 的操作。從頭開始寫,寫到末尾就又回到開頭循環寫,如下面這個圖所示:

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

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

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

redo日誌緩衝區

redo 日誌並不是直接寫入磁碟的,而是先寫入到快取區,我們把這個緩衝區叫做 redo日誌緩衝區。在伺服器啟動時就向作業系統申請了一大片稱之為 redo log buffer 的連續記憶體空間,我們也可以簡稱為log buffer。這片記憶體空間被劃分成若干個連續的 redo log block,如下圖所示:

在 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 相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支援。若文中有所錯誤之處,還望提出,謝謝。