閱讀144 返回首頁    go 阿裏雲 go 技術社區[雲棲]


MySQL5.7:崩潰恢複“”優化“” 所帶來的3例退化

背景

在MySQL5.7之前的版本中,每次崩潰恢複時都需要打開數據目錄下所有的ibd文件,校驗其有效性並內存中創建表空間鏈表。當表的數量特別多的時候, 會嚴重影響到崩潰恢複的性能。

為了解決這個問題,MySQL5.7版本中增加了新的日誌類型MLOG_FILE_NAME及MLOG_CHECKPOINT。前者記錄了被修改後的Ibd文件名,後者記錄了最近一次checkpoint的LSN點。通過掃描Redo日誌,InnoDB可以找到在最近一次checkpoint之後修改過的數據文件,這樣在崩潰恢複時就無需打開所有的表空間。

但據我所知,這個特性至少引入了三個問題。以下簡單為大家分享下

Log Parse Buffer溢出(bug#83245)

第一個問題是在做checkpoint時,會在一個mtr裏將這期間修改的文件名記錄到redo中,如果涉及的文件過多的話,就會導致一個mtr非常巨大,在崩潰恢複時,用於解析log的buffer(大小為2MB)會溢出。這個bug在MySQL5.7.18版本被修複。

為了避免這個問題,在做checkpoint寫文件名時,如果已產生的log大小超過4個page size(LOG_CHECKPOINT_FREE_PER_THREAD), 就將當前日誌提交掉並重新開啟mtr(這中間不釋放log_sys->mutex)

另一個修改是在崩潰恢複parse redo log的時候,如果一個log group中都是MLOG_FILE_NAME, 則直接推進recovered_offset/lsn,相當直接推進recover的點,這樣在parse buffer裏就無需存儲整個mtr的日誌。

詳細修複補丁見這個commit

崩潰恢複的性能退化(bug#80788)

該特性帶來的另外一個嚴重問題是崩潰恢複的性能退化,尤其是在表少,但需要恢複的日誌量很大的時候。這是因為在崩潰恢複時最少掃描兩次日誌,最多要重複掃描三次,以下摘自commit log的解釋

    First scan: Scan all the redo logs from checkpoint lsn and process
    only MLOG_FILE_* records during first scan. It scans till the
    last MLOG_CHECKPOINT.

    Second scan: Scan all redo logs from checkpoint lsn and add log
    records to hash table. It verifies whether space id is having
    corresponding MLOG_FILE_NAME record. If the hash table heap memory
    is reached the threshold then stop adding records to hash but it
    continues to scan till end of the redo log file.

    Third scan: Scan all redo logs from checkpoint lsn and add log records
    to hash table only if the tablespace exists. If the heap memory reached
    the threshold then simultaneous scan and apply will happen.

我在report了bug後,很快官方就確認了,但直到5.7.19版本才fix了這個問題,對崩潰恢複有要求的同學最好盡快升級到這個版本。

簡單看了下官方的修複,思路也比較簡單,將第一次和第二次掃描合並,這樣最少一次,最多兩次掃描(buffer pool不夠大的時候)。

Patch鏈接如下:
COMMIT 1
COMMIT 2

更高的fil_system::mutex衝突(bug#85304)

由於需要在redo中記錄修改的文件名,每次在做數據變更時,都會去調用函數mtr_t::set_named_space,對於用戶表空間,會進一步的調用函數mtr_t::lookup_user_space-> fil_space_get 通過space id來獲得表空間的fil_space_t對象,這需要fil_system::mutex的保護。在高並發下可能導致比較激烈的鎖衝突。

目前MySQL5.7還沒有修複計劃(估計也不會去修了..),但8.0版本已經將 WL#7142的工作整體刪除了,並實現了另外一套機製來加速崩潰恢複(後麵單獨介紹)。而針對fil_system::mutex的通用場景衝突也在優化過程中。我們可以期待下MySQL8.0版本 :)

最後更新:2017-08-13 22:33:52

  上一篇:go  LinkedBlockingQueue源碼解讀
  下一篇:go  JDK 1.8 ArrayBlockingQueue源碼解讀(不含迭代器)