MySQL的Double Write並不難理解

問題引入- 斷電了

今天為大家介紹一個新的名詞: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會檢查出:這個數據頁是個不完整的數據頁。

想了解更多崩潰恢復的知識,可以看這篇筆記: //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函數可以看我這篇筆記://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 串講(已發佈)
Tags: