一起看下MySQL的崩潰恢復到底是怎麼回事

8563084發表於2020-12-11

本文稍微有點晦澀、但是看過之後你就能Get到MySQL的崩潰恢復到底是怎麼做的!

文章公號 首發!連載中!關注微信公號回覆:“抽獎” 還可參加抽?活動

回顧

在這篇文章之前,白日夢跟你分享了什麼是redo log、以及redo log的作用、redo log的刷盤機制等知識點。簡單來說就是redo log是MySQL的事物日誌。比如你執行一條update語句,在你提交事物之前MySQL就會在redo log中記錄下你這條SQL對XXX表空間XXX資料頁的XXX偏移量做了XXX更新。


思考一個問題

為什麼在你當update時,事物提交之前先不斷的寫redo log呢?

如果你看過白日夢前面介紹buffer pool的文章,這個問題的答案想必你也能很快的想出來:MySQL為了提高效能,你對它資料行的增、刪、改操作其實都優先發生在記憶體(Buffer Pool)中。那你想,假如你update了某些資料,Buffer Pool中的資料頁也就會被你改成髒資料頁。那萬一你剛修改完並提交了事物,還沒來得及將資料落盤MYSQL就當機了怎麼辦?

當MySQL重啟的時候需要把方才修改的內容恢復出來吧,不然資料就不一致了。那怎麼恢復呢?就藉助redo log恢復。因為前面說了,當你begin事物開始操作時,會先寫redo log,在運算元據頁。這個資料恢復的過程也叫做重做。


checkponit機制

隨著MySQL的執行,Buffer Pool中的資料頁會被修改成髒資料頁,當你開啟事物進行一系列的操作時MySQL會為你不停的記錄一堆日誌,拿redolog來說,rodo log也是需要往先往記憶體中寫,再以塊的形式重新整理回磁碟。

無論怎樣都會存在這樣一箇中間過程:記憶體中存在髒資料頁、和髒日誌未來得及重新整理回磁碟。

而本小節中要說的Checkpoint機制就是將這些髒資料重新整理回磁碟的機制,即只要發生Checkpoint,就要將髒資料重新整理回磁碟,反過來,當MySQL重啟時會去找Checkpoint,並且根據Checkpoint的特性。MySQL可以明確的知道checkponit之前的髒資料已經落過盤了,重啟時沒必要進行重做。

看到這裡你已經大概知道Checkpoint是什麼了。我們在稍微總結一下Checkpoint機制的作用:

1、所謂的崩潰恢復,其實就是MySQL重啟時照著redo log中的最後一次Checkpoint之後的日誌回放一遍

2、因為Checkpoint會不斷的更新,並且MySQL重啟時只需要對Checkpoint之後的資料進行恢復,所以Checkpoint會縮短MySQL重啟的時間。

3、因此每次進行Checkpoint時buffer pool中的髒資料頁、redo log中的髒日誌都會落盤。所以Checkpoint實際上起到了為這兩者進行瘦身的作用。維持兩個的可用性。


Checkpoint的種類及觸發條件

有兩種:Checkpoint

1、Sharp(急劇的) Checkpoint

觸發時機:比如當MySQL關閉時,或者是切換要寫的redo log時,會一次性將所有的髒日誌全部重新整理到磁碟中,這種模式下會對MySQL的效能帶來較大的影響。

2、Fuzzy(模糊的) Checkpoint

這種模式下的Checkpoint每次僅將部分髒日誌重新整理到磁碟中

觸發條件1:Master Thread Checkpoint

由master執行緒控制,每秒或每10秒刷入一定比例的髒頁到磁碟。

觸發條件2:FLUSH_LRU_LIST Checkpoint

從MySQL5.6開始可通過 innodb_page_cleaners 變數指定專門負責髒頁刷盤的page cleaner執行緒的個數,該執行緒的目的是為了保證lru列表有可用的空閒頁。

觸發條件3:async/sync flush Checkpoint

同步刷盤還是非同步刷盤。例如還有非常多的髒頁沒刷到磁碟,這時候會選擇同步刷到磁碟,但這很少出現;如果髒頁不是很多,可以選擇非同步刷到磁碟,如果髒頁很少,可以暫時不刷髒頁到磁碟

觸發條件4:dirty page too much Checkpoint

髒頁太多時強制觸發檢查點,目的是為了保證快取有足夠的空閒空間。too much的比例由變數 innodb_max_dirty_pages_pct 控制,MySQL 5.6預設的值為75,即當髒頁佔緩衝池的百分之75後,就強制刷一部分髒頁到磁碟。


LSN

LSN全稱是:log sequence number。

關於什麼是LSN沒什麼難以理解的,它就是一個序列號。並且表空間中的資料頁、快取頁、記憶體中的rodo log、磁碟中的redo log以及checkponit都有LSN標記。

LSN又啥用呢?比如MySQL重啟時會對比資料頁的LSN和redo log的LSN的大小,如果前者的LSN比後者小。說明資料頁中缺失了一部分資料。如果滿足其他資料恢復的條件,MySQL就會將LSN之後的這些redo 進行一次回方,完成資料的恢復。

舉個需要重做的例子:假設你使用的是MySQL叢集,從庫通過binlog同步主庫的資料。

理論上:你開啟了事物,然後一頓操作然後提交事物。在你操作的過程中MySQL會為你記錄undo log、redo log parpare、binlog、redo log commit。(兩階段提交)

然而不幸的是,當MySQL寫完binlog、且未來得及寫 redo log commit完成的事物最終的提交就掛了。

那MYSQL重啟,由於未來得及commit,髒資料頁沒有重新整理到磁碟上,所以重啟時得到的資料時不準確的,但是,實際上MySQL會根據方才的redo log重做。因為binlog已經寫完了,那就意味著從庫已經完成了資料的同步。如果它不重做的話,它相對於從庫就缺失了一部分資料,導致主從資料不一致。

關於這個例子,後面的文章中我還會非常詳細的說。

當然關於LSN你還得了解:

在MySQL 5.6.3之前,LSN是一個4位元組的無符號整數。當重做日誌檔案的大小限制從4GB增加到512GB時,由於需要額外的位元組來儲存額外的大小資訊,因此LSN在MySQL 5.6.3中變成了8位元組的無符號整數。

你可以執行如下SQL檢視你的MySQL的LSN標記記錄情況:

  show engine innodb status\G

為了更徹底的理解LSN、checkpoint我們可以一起看下面幾張圖:

第一張:我去網上找講LSN的帖子時發現的很多文章使用下面這張圖

但是這張圖的中我用方框圈出來的地方實際上搞錯了。圖中的LSN應該放在倒數第二條線上。

我參照上圖模改一套版圖、簡述一下表達的意思如下:

前面說了表空間中的資料頁、記憶體中的快取頁、記憶體中的redo log、磁碟中的redo log、checkpoint它們五者都有記錄LSN。所以你可以看到,在12:00:00時刻,它們五者中的LSN都是100。

然後在12:00:00時刻你開啟了一個事物,執行update語句,之前的文章說過,你的CRUD都優先發生在記憶體中,也就是buffer pool中。並且在修改記憶體中的資料前先記錄redo log,所以buffer pool中的快取頁和記憶體中的redo log的LSN率先被更新成110。

緊接著你的事物中又執行了delete語句,同樣的道理記憶體中的redo log和快取頁的LSN被更新為150。接著時間來到了12:00:01。觸發了由引數innodb_flush_log_at_timeout(預設1s)redo log刷盤機制。redo log會部分落盤,所以上圖中的磁碟上的redo log的lSN更新為150。

接著你的事物中又執行了一次delete語句。同理記憶體中的快取頁和記憶體中的redo log的LSN被更新成了300。

終於checkpoint機制觸發了!checkpoint機制的觸發意味著要將記憶體中所有髒資料落盤。因此記憶體中的快取頁磁碟成為磁碟上的資料頁,也就是說磁碟上的資料頁的LSN變成300。同理磁碟上的redo log的LSN變成300。checkpoint的LSN也更新成300。

然而你的事物並沒有結束,你在12:00:02時刻又insert了一條資料,同上面的原因記憶體中的快取頁、記憶體中的redo log 的LSN被更新成800。

終於你的要提交事物了!預設情況下,根據上一篇文章中跟大家分享的雙1配置。事物提交時,redo log會落盤,但是記憶體中的髒資料並不會落盤。所以磁碟上的redo log LSN被更新成800。也就是說此時除了表空間中的資料頁的LSN、checkpoint的LSN其他的LSN均已達到最新的800。

直到最後checkpoint再次出現。這五者LSN重新保持一致。

上述說的什麼表空間、資料頁、快取頁之前的文章中都說過了。忘記了可以自行翻看哈。


參考:

《MySQL技術內幕》

https://dev.mysql.com/doc/refman/5.7/en/innodb-Checkpoints.html

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_Checkpoint

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_flush

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_lsn

https://blog.csdn.net/gdj0001/article/details/83510447

推薦閱讀

  1. 大家常說的基數是什麼?(已釋出)
  2. 講講什麼是慢查!如何監控?如何排查?(已釋出)
  3. 對NotNull欄位插入Null值有啥現象?(已釋出)
  4. 能談談 date、datetime、time、timestamp、year的區別嗎?(已釋出)
  5. 瞭解資料庫的查詢快取和BufferPool嗎?談談看!(已釋出)
  6. 你知道資料庫緩衝池中的LRU-List嗎?(已釋出)
  7. 談談資料庫緩衝池中的Free-List?(已釋出)
  8. 談談資料庫緩衝池中的Flush-List?(已釋出)
  9. 瞭解髒頁刷回磁碟的時機嗎?(已釋出)
  10. 用十一張圖講清楚,當你CRUD時BufferPool中發生了什麼!以及BufferPool的優化!(已釋出)
  11. 聽說過表空間沒?什麼是表空間?什麼是資料表?(已釋出)
  12. 談談MySQL的:資料區、資料段、資料頁、資料頁究竟長什麼樣?瞭解資料頁分裂嗎?談談看!(已釋出)
  13. 談談MySQL的行記錄是什麼?長啥樣?(已釋出)
  14. 瞭解MySQL的行溢位機制嗎?(已釋出)
  15. 說說fsync這個系統呼叫吧! (已釋出)
  16. 簡述undo log、truncate、以及undo log如何幫你回滾事物! (已釋出)
  17. 我勸!這位年輕人不講MVCC,耗子尾汁! (已釋出)


相關文章