MySQL的Double Write並不難理解

賜我白日夢發表於2021-05-08

問題引入- 斷電了

今天為大家介紹一個新的名詞:double write。

相信你還記得,我之前有寫筆記跟大家分享過,在MySQL組織資料的基本單位是存在於磁碟上的資料頁。資料頁被讀取到記憶體(Buffer Pool)中後被稱為快取頁。預設情況下每個資料頁的大小是16kb,資料頁中儲存的就是一行行真實的記錄,也叫做資料行。

mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+

當有寫操作對Buffer Pool中的資料頁做了更新,我們把這種被改變過的資料頁叫做:髒頁。它是需要被重新整理會磁碟的。這時問題就來了,對於計算機硬體或者是作業系統來說,每次原子IO操作的吞吐量量是小於16KB的,一般每個扇區的大小是512位元組。

# 檔案系統塊大小:一般為4k
~]# getconf PAGESIZE
4096

~]# fdisk -l

磁碟 /dev/sda:53.7 GB, 53687091200 位元組,104857600 個扇區
Units = 扇區 of 1 * 512 = 512 bytes
扇區大小(邏輯/物理):512 位元組 / 512 位元組
I/O 大小(最小/最佳):512 位元組 / 512 位元組
磁碟標籤型別:dos
磁碟識別符號:0x000a88ef

 裝置 Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048     2099199     1048576   83  Linux
/dev/sda2         2099200   104857599    51379200   8e  Linux LVM

磁碟 /dev/mapper/centos-root:50.5 GB, 50457477120 位元組,98549760 個扇區
Units = 扇區 of 1 * 512 = 512 bytes
扇區大小(邏輯/物理):512 位元組 / 512 位元組
I/O 大小(最小/最佳):512 位元組 / 512 位元組


磁碟 /dev/mapper/centos-swap:2147 MB, 2147483648 位元組,4194304 個扇區
Units = 扇區 of 1 * 512 = 512 bytes
扇區大小(邏輯/物理):512 位元組 / 512 位元組
I/O 大小(最小/最佳):512 位元組 / 512 位元組

那這麼看來,當你想把一個16K大小的資料頁寫入到磁碟中時,結果剛寫了4k,突然斷電機器當機了。那此時只有一部分是寫入成功的。這就是大家常說的: partial page write

不一會 - 來電了

還是接著上面的描述說,不一會電源正常了,開機重啟MYSQL,這時MySQL會進入到崩潰恢復的階段。

正常的崩潰恢復流程是:

  1. 將資料頁從磁碟中讀入到記憶體中
  2. 檢查資料頁中的LSN標記和redo log中的LSN誰更新,如果相同表示此時的資料頁中的資料就是最新的,如果redo log的LSN比資料頁中的LSN大,說明資料頁中的資料是過時的資料,按redo重做出一份最新資料

但是現在問題是:因為MySQL的Crash是由斷電引發的,作業系統都沒來得及將資料頁完整的寫入到磁碟中,導致崩潰恢復的第一步就失敗了,因為MySQL會檢查出:這個資料頁是個不完整的資料頁。

想了解更多崩潰恢復的知識,可以看這篇筆記: https://mp.weixin.qq.com/s/6dQnlvjqOo6A0e_h8vST3w

Double write工作流程

結合double write來看一下一條update sql的執行流程

Step1: 滿足update條件的資料頁如果不再Buffer Pool中,就進行一次IO操作,將其載入進磁碟。

Step2: 將該資料頁修改成髒頁。

Step3: 當需要將緩衝池的髒頁重新整理到 data file 時,並不直接寫到資料檔案中,而是先拷貝至記憶體中的 double write buffer。

Step4: 接著從 double write buffer 分兩次寫入磁碟共享表空間中,每次寫入 1MB,並馬上呼叫 fsync 函式,同步到磁碟,避免緩衝帶來的問題。

Step5: 完成Step2後,再將兩次寫緩衝區寫入其對應的單獨的資料檔案。

關於fsync函式可以看我這篇筆記:https://mp.weixin.qq.com/s/tyxd64gGa_SmR6c9vrwf1w

恢復的過程

  1. 將資料頁從磁碟中讀入到記憶體中

  2. 檢查到資料頁損壞了,嘗試通過double write恢復資料。

  3. 如果 double write 的資料是完整的,用 double buffer 的資料頁替換壞掉的資料頁。

那,如果 double write 中的資料頁被寫壞了怎麼辦?

其實沒關係,因為是先往共享表空間中寫double write資料頁,再往各個表對應的表空間檔案中寫實際的資料頁,如果double write中的資料頁壞點了,那恰恰說明,各個表對應的表空間檔案中的資料頁沒壞!恢復的流程不會被打斷!

配置引數

# 檢視是否啟用了double write,以及相關引數
mysql> SHOW VARIABLES LIKE 'innodb_doublewrite%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_doublewrite            | ON    |
| innodb_doublewrite_batch_size | 120   |
+-------------------------------+-------+
2 rows in set (0.02 sec)

# 查詢double write的使用情況
mysql> SHOW STATUS LIKE 'innodb_dblwr_%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Innodb_dblwr_pages_written | 14615 |   #從BP寫入到dblwr的page數
| Innodb_dblwr_writes        | 636   |   #寫檔案的次數
+----------------------------+-------+
2 rows in set (0.02 sec)

疑問

看到這裡你肯定已經知道了double write解決了資料頁被寫壞的情況,也就是說,redo log不能對一個本身就壞掉的資料頁進行重做。

但是,不知道你有沒有這樣的疑問,那redo log也是以檔案的形式存在於磁碟上的,那假如在write redo log時,斷電了呢?那redo log不也被損壞了?那還崩潰恢復個錘子?

答:是這樣的:事務產生的redo log先被組織成redo log block。並且redo log block其實就在redo log buffer 中。而redo log block的大小==作業系統一次原子IO的吞吐量512位元組就像下圖這樣:

推薦閱讀

  1. MySQL的修仙之路,圖文談談如何學MySQL、如何進階!(已釋出)
  2. 面前突擊!33道資料庫高頻面試題,你值得擁有!(已釋出)
  3. 大家常說的基數是什麼?(已釋出)
  4. 講講什麼是慢查!如何監控?如何排查?(已釋出)
  5. 對NotNull欄位插入Null值有啥現象?(已釋出)
  6. 能談談 date、datetime、time、timestamp、year的區別嗎?(已釋出)
  7. 瞭解資料庫的查詢快取和BufferPool嗎?談談看!(已釋出)
  8. 你知道資料庫緩衝池中的LRU-List嗎?(已釋出)
  9. 談談資料庫緩衝池中的Free-List?(已釋出)
  10. 談談資料庫緩衝池中的Flush-List?(已釋出)
  11. 瞭解髒頁刷回磁碟的時機嗎?(已釋出)
  12. 用十一張圖講清楚,當你CRUD時BufferPool中發生了什麼!以及BufferPool的優化!(已釋出)
  13. 聽說過表空間沒?什麼是表空間?什麼是資料表?(已釋出)
  14. 談談MySQL的:資料區、資料段、資料頁、資料頁究竟長什麼樣?瞭解資料頁分裂嗎?談談看!(已釋出)
  15. 談談MySQL的行記錄是什麼?長啥樣?(已釋出)
  16. 瞭解MySQL的行溢位機制嗎?(已釋出)
  17. 說說fsync這個系統呼叫吧! (已釋出)
  18. 簡述undo log、truncate、以及undo log如何幫你回滾事物! (已釋出)
  19. 我勸!這位年輕人不講MVCC,耗子尾汁! (已釋出)
  20. MySQL的崩潰恢復到底是怎麼回事? (已釋出)
  21. MySQL的binlog有啥用?誰寫的?在哪裡?怎麼配置 (已釋出)
  22. MySQL的bin log的寫入機制 (已釋出)
  23. 刪庫後!除了跑路還能幹什麼?(已釋出)
  24. 自導自演的面試現場,趣學資料庫的10種檔案(已釋出)
  25. 大型面試現場:一條update sql執行都經歷什麼?(已釋出)
  26. 大型翻車現場:如何實現記錄存在的話就更新,如果記錄不存在的話就插入。(已釋出)
  27. 視訊+圖文串講:MySQL 行鎖、間隙鎖、Next-Key-Lock、以及實現記錄存在的話就更新,如果記錄不存在的話就插入如何保證併發安全(已釋出)
  28. 自導自演的面試現場:說說char 和 varchar的區別你瞭解多少?。(已釋出)
  29. 自導自演的面試現場之--你竟然不瞭解MySQL的組提交?。(已釋出)
  30. 全網最清楚的:MySQL的insert buffer和change buffer 串講(已釋出)

相關文章