天天看點

MongoDB複制集同步原了解析

intial sync,可以了解為全量同步

replication,追同步源的oplog,可以了解為增量同步

secondary節點當出現如下狀況時,需要先進行全量同步

oplog為空

local.replset.minvalid集合裡_initialsyncflag字段設定為true

記憶體标記initialsyncrequested設定為true

這3個場景分别對應

新節點加入,無任何oplog,此時需先進性initial sync

initial sync開始時,會主動将_initialsyncflag字段設定為true,正常結束後再設定為false;如果節點重新開機時,發現_initialsyncflag為true,說明上次全量同步中途失敗了,此時應該重新進行initial sync

當使用者發送resync指令時,initialsyncrequested會設定為true,此時會重新開始一次initial sync

intial sync流程

全量同步開始,設定minvalid集合的_initialsyncflag

擷取同步源上最新oplog時間戳為t1

全量同步集合資料 (耗時)

擷取同步源上最新oplog時間戳為t2

重放[t1, t2]範圍内的所有oplog

擷取同步源上最新oplog時間戳為t3

重放[t2, t3]範圍内所有的oplog

建立集合所有索引 (耗時)

擷取同步源上最新oplog時間戳為t4

重放[t3, t4]範圍内所有的oplog

全量同步結束,清除minvalid集合的_initialsyncflag

MongoDB複制集同步原了解析

producer thread,這個線程不斷的從同步源上拉取oplog,并加入到一個blockqueue的隊列裡儲存着。

replbatcher thread,這個線程負責逐個從producer thread的隊列裡取出oplog,并放到自己維護的隊列裡。

sync線程将replbatcher thread的隊列分發到預設16個replwriter線程,由replwriter thread來最終重放每條oplog。

問題來了,為什麼一個簡單的『拉取oplog并重放』的動作要搞得這麼複雜?

為什麼不将拉取的oplog直接分發給replwriter thread,而要多一個replbatcher線程來中轉?

新加入節點時,可以通過實體複制的方式來避免initial sync,将primary上的dbpath拷貝到新的節點,直接啟動,這樣效率更高。

生産環境,最好通過db.printslavereplicationinfo()來監控主備同步滞後的情況,當secondary落後太多時,要及時調查清楚原因。

當secondary同步滞後是因為主上并發寫入太高導緻,(db.serverstatus().metrics.repl.buffer.sizebytes持續接近db.serverstatus().metrics.repl.buffer.maxsizebytes),可通過調整secondary上replwriter并發線程數來提升。