問題引入- 斷電了
今天為大家介紹一個新的名詞: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會進入到崩潰恢復的階段。
正常的崩潰恢復流程是:
- 將資料頁從磁碟中讀入到記憶體中
- 檢查資料頁中的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
恢復的過程
-
將資料頁從磁碟中讀入到記憶體中
-
檢查到資料頁損壞了,嘗試通過double write恢復資料。
-
如果 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位元組
就像下圖這樣:
推薦閱讀
- MySQL的修仙之路,圖文談談如何學MySQL、如何進階!(已釋出)
- 面前突擊!33道資料庫高頻面試題,你值得擁有!(已釋出)
- 大家常說的基數是什麼?(已釋出)
- 講講什麼是慢查!如何監控?如何排查?(已釋出)
- 對NotNull欄位插入Null值有啥現象?(已釋出)
- 能談談 date、datetime、time、timestamp、year的區別嗎?(已釋出)
- 瞭解資料庫的查詢快取和BufferPool嗎?談談看!(已釋出)
- 你知道資料庫緩衝池中的LRU-List嗎?(已釋出)
- 談談資料庫緩衝池中的Free-List?(已釋出)
- 談談資料庫緩衝池中的Flush-List?(已釋出)
- 瞭解髒頁刷回磁碟的時機嗎?(已釋出)
- 用十一張圖講清楚,當你CRUD時BufferPool中發生了什麼!以及BufferPool的優化!(已釋出)
- 聽說過表空間沒?什麼是表空間?什麼是資料表?(已釋出)
- 談談MySQL的:資料區、資料段、資料頁、資料頁究竟長什麼樣?瞭解資料頁分裂嗎?談談看!(已釋出)
- 談談MySQL的行記錄是什麼?長啥樣?(已釋出)
- 瞭解MySQL的行溢位機制嗎?(已釋出)
- 說說fsync這個系統呼叫吧! (已釋出)
- 簡述undo log、truncate、以及undo log如何幫你回滾事物! (已釋出)
- 我勸!這位年輕人不講MVCC,耗子尾汁! (已釋出)
- MySQL的崩潰恢復到底是怎麼回事? (已釋出)
- MySQL的binlog有啥用?誰寫的?在哪裡?怎麼配置 (已釋出)
- MySQL的bin log的寫入機制 (已釋出)
- 刪庫後!除了跑路還能幹什麼?(已釋出)
- 自導自演的面試現場,趣學資料庫的10種檔案(已釋出)
- 大型面試現場:一條update sql執行都經歷什麼?(已釋出)
- 大型翻車現場:如何實現記錄存在的話就更新,如果記錄不存在的話就插入。(已釋出)
- 視訊+圖文串講:MySQL 行鎖、間隙鎖、Next-Key-Lock、以及實現記錄存在的話就更新,如果記錄不存在的話就插入如何保證併發安全(已釋出)
- 自導自演的面試現場:說說char 和 varchar的區別你瞭解多少?。(已釋出)
- 自導自演的面試現場之--你竟然不瞭解MySQL的組提交?。(已釋出)
- 全網最清楚的:MySQL的insert buffer和change buffer 串講(已釋出)