MongoDB Secondary同步慢問題分析

問題背景

最近生產環境出現多次Primary寫入QPS太高,導致Seconary的同步無法跟上的問題(Secondary上的最新oplog時間戳比Primary上最舊oplog時間戳小),使得Secondary變成RECOVERING狀態,這時需要人工介入處理,向Secondary發送resync命令,讓Secondary重新全量同步一次。

同步過程

下圖是MongoDB數據同步的流程
這裏寫圖片描述

Primary上的寫入會記錄oplog,存儲到一個固定大小的 capped collection 裏,Secondary主動從Primary上拉取oplog並重放應用到自身,以保持數據與Primary節點上一致。

initial sync

新節點加入(或者主動向Secondary發送resync)時,Secondary會先進行一次initial sync,即全量同步,遍歷Primary上的所有DB的所有集合,將數據拷貝到自身節點,然後讀取『全量同步開始到結束時間段內』的oplog並重放。全量同步不是本文討論的重點,將不作過多的介紹。

tailing oplog

全量同步結束後,Secondary就開始從結束時間點建立tailable cursor,不斷的從同步源拉取oplog並重放應用到自身,這個過程並不是由一個線程來完成的,mongodb爲了提升同步效率,將拉取oplog以及重放oplog分到了不同的線程來執行。

  • producer thread,這個線程不斷的從同步源上拉取oplog,並加入到一個BlockQueue的隊列裏保存着,BlockQueue最大存儲240MB的oplog數據,當超過這個閾值時,就必須等到oplog被replBatcher消費掉才能繼續拉取。
  • replBatcher thread,這個線程負責逐個從producer thread的隊列裏取出oplog,並放到自己維護的隊列裏,這個隊列最多允許5000個元素,並且元素總大小不超過512MB,當隊列滿了時,就需要等待oplogApplication消費掉。
  • oplogApplication會取出replBatch thread當前隊列的所有元素,並將元素根據docId(如果存儲引擎不支持文檔鎖,則根據集合名稱)分散到不同的replWriter線程,replWriter線程將所有的oplog應用到自身;等待所有oplog都應用完畢,oplogApplication線程將所有的oplog順序寫入到local.oplog.rs集合。

producer的buffer和apply線程的統計信息都可以通過db.serverStatus().metrics.repl來查詢到,在測試過程中,向Primary模擬約10000 qps的寫入,觀察Secondary上的同步,寫入速率遠小於Primary,大致只有3000左右的qps,同時觀察到 producer的buffer很快就達到飽和,可以判斷出oplog重放的線程跟不上 。

默認情況下,Secondary採用16個replWriter線程來重放oplog,可通過啓動時設置replWriterThreadCount參數來定製線程數,當提升線程數到32時,同步的情況大大改觀,主備寫入的qps基本持平,主備上數據同步的延時控制在1s以內,進一步驗證了上述結論。

改進思路

如果因Primary上的寫入qps很高,經常出現Secondary同步無法追上的問題,可以考慮以下改進思路

參考資料

capped collection
SERVER-18908
修改oplog的大小
阿里雲MongoDB數據庫

來自: http://blog.yunnotes.net/index.php/mongodb-scondary-cannot-catchup/

發佈了107 篇原創文章 · 獲贊 22 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章