MySQL5.7:PageCleaner的刷髒問題

zhaiwx_yinfeng發表於2016-05-10

之前我已經寫過一篇部落格,討論過在flush LRU_LIST/FLUSH_LIST時,5.7對其做的優化,總的來說,就是使用類似Hazard Pointer的方式,避免在flush的過程中重複掃描LIST,將時間複雜度從O(N*N)下降到了O(N)。有興趣的同學可以翻閱下這篇部落格:http://mysqllover.com/?p=1031

本文的目的主要是補充下5.7目前所做的多個page cleaner的實現思路,社群相關的bug討論,以及我近期對page cleaner所做的一些優化工作。

支援多個Page cleaner

為了提升擴充套件性和刷髒效率,在5.7.4版本里引入了多個page cleaner執行緒。從而達到並行刷髒的效果。

在該版本中,Page cleaner並未和buffer pool繫結,其模型為一個協調執行緒 + 多個工作執行緒,協調執行緒本身也是工作執行緒。因此如果innodb_page_cleaners設定為8,那麼就是一個協調執行緒,加7個工作執行緒

協調執行緒的入口函式為buf_flush_page_cleaner_coordinator

工作執行緒的入口函式為buf_flush_page_cleaner_worker

在啟動時會初始化一個slot陣列,大小為buffer pool instance的個數(buf_flush_page_cleaner_init)。

協調執行緒在決定了需要flush的page數和lsn_limit後,會設定slot陣列,將其中每個slot的狀態設定為PAGE_CLEANER_STATE_REQUESTED, 並設定目標page數及lsn_limit,然後喚醒worker執行緒 (pc_request)

worker執行緒被喚醒後,從slot陣列中取一個未被佔用的slot,修改其狀態,表示已被排程,然後對該slot所對應的buffer pool instance進行操作。

為了支援對單個bp instance進行LRU/FLUSH_LIST的重新整理,對原有程式碼做了大量的改動,worker執行緒可以直接呼叫buf_flush_LRU_list 及buf_flush_do_batch 指定buffer pool進行flush操作。 互相之間不干擾,因此可以並行刷髒。 改動整體而言比較簡單。

由於當前版本page cleaner是不和buffer pool繫結的,因此,最好不要將page cleaner的個數設定的大於buffer pool instance的個數。

如果當前例項不活躍,即沒有負載時,則單獨由協調執行緒做100%的刷髒。

worklog:

http://dev.mysql.com/worklog/task/?id=6642

程式碼比較簡單,不展開闡述,感興趣的可以讀讀補丁:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/7189

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/7244

進一步改進Multi Page Cleaner

在MySQL 5.7.5裡進一步改進了multi page cleaner特性,讓其支援在crash recovery和shutdown時能夠應用多個page cleaner特性,來加快崩潰恢復和關閉例項的速度。

實現方式也比較簡單,例如對於recovery過程中,如果需要刷髒時,不主動刷髒,而是喚醒page cleaner,然後等待page cleaner完成。

PATCH:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8489

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8522

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8532

社群相關BUG

http://bugs.mysql.com/bug.php?id=68481

期望允許page cleaner做furious flush,而不是每秒做一次,對於寫入負載很猛的場景,很容易到達同步checkpoint點

http://bugs.mysql.com/bug.php?id=70500

不管是否active,總是做LRU FLUSH。 因為在某些場景下,可能沒什麼使用者負載,checkpoint age很低,但諸如purge操作需要很多free page時, 這時候我們其實期望page cleaner 負責清出更多的空閒block。

事實上, webscalesql已經這麼做了。感興趣的可以直接去checkout webscalesql的程式碼看看

http://bugs.mysql.com/bug.php?id=71411

buf_do_LRU_batch函式的返回值表示從LRU上flush的髒頁數,但卻把壓縮表上驅逐的解壓頁也納入了統計。這可能會引起某些innodb_metrics的錯誤值。 這還會影響到page cleaner的刷髒策略。

http://bugs.mysql.com/bug.php?id=74637

讓page cleaner的重新整理策略更加自適應。應該根據redo space 和free list做出自適應的page cleaner策略調整,我(id zhai weixiang) 在bug中也回覆了可行的策略:對於lru_list,percona server有更加聰明的做法,獨立出LRU執行緒並根據free list長度調整sleep時間;對於flush_list,我們可以對sleep的時間做動態調整,根據max_modified_age_sync來進行估算。

可行的改進

針對page cleaner的問題,我基於阿里內部的版本中做了些修改 (感興趣的阿里同學可以找我拿測試branch),大概包含如下幾點:

  1. 分拆LRU FLUSH到獨立執行緒,根據FREE LIST長度自適應調整刷LRU策略 (from percona)
  2. PAGE CLEANER支援multi page cleaner (from 5.7)。並根據redo space 計算自適應的刷髒頻率
  3. 從使用者執行緒移除SINGLE PAGE FLUSH,總是由LRU FLUSH執行緒來釋放空閒page.
  4. MULTI PAGE CLEANER依然會引起double write buffer的單點競爭。為了解決這個問題,引入多個double write file,每個double write buffer檔案對應特定的buffer pool instance.


相關文章