天天看點

MongoDB副本集復原那些事

復原(rollback)操作是mongodb副本集發生一些異常主備切換後可能發生的現象。復原操作會撤銷在目前節點上已執行的一些修改操作。

mongodb副本集節點上有個同步線程,負責拉取需要同步的oplog。被拉取oplog的節點稱作同步源。那麼,要復原,首先要有一個同步源。

平時我們都說主備同步主備同步,那同步源肯定是主節點了?其實不一定,mongodb很早就支援了鍊式複制,即備節點可以從另外一個備節點拉取oplog,而不隻從主節點拉取。這樣一來可以減少主節點的負載,二來各節點可以選擇離自己近的節點進行同步。當然,在某些情況下,這可能會導緻一些備節點的延遲變大。鍊式複制可以通過以下指令來打開或關閉:

secondary節點會根據以下原則選擇一個同步源:

如果之前有通過指令replsetsyncfrom指定了同步源,那麼使用此同步源

由于後續需要根據到其他節點的ping值(通過心跳進行統計)資訊進行選擇,這裡會判斷一下是否已有足夠的資訊,需要等待更多的心跳包,如果不需要,繼續,否則直接傳回,等下次需要選擇時再看

如果沒有開啟chained replication(鍊式複制),那麼選擇primary

通過兩輪選擇,基于以下規則選擇一個ping值最低的節點:

如果自己可以建索引,那麼隻能從同樣可以建索引的節點同步

oplog的時間戳比我新(這裡是擷取該節點上次心跳包裡帶的appliedoptime的時間戳進行比較)

不在黑名單中(注:何時将同步節點加進黑名單?1. 連接配接不上該節點,加10s黑名單;2. 落後該節點太多無法繼續同步,加1min黑名單)

其中在第一輪選擇中,會額外考慮以下條件:

擁有投票權的節點隻能從同樣擁有投票權的節點同步

不能從hidden節點同步

不能從落後primary太多(超過配置的maxsyncsourcelagsecs)的節點同步

不能從配置了比自己擁有更大delay的節點同步

如果第一輪沒有選出合适的節點,那麼再進行第二輪選擇,放寬上述條件的限制。

回到復原觸發條件。同步線程已經選擇出了一個同步源,它向同步源發起一個find請求,查詢大于等于其最新的oplog時間戳的oplog。如果發生以下兩種情況,那麼需要復原:

在同步源上沒有查到比其更新的oplog(我們剛剛通過一系列麻煩的規則選出它作為同步源,但是我們的oplog卻比它還新)

傳回的的第一條oplog和其最新的oplog的optime和hash都不同,注意這裡是比較整個optime,即除了時間戳之外還包括term,首先會比較term,如果term不同,那就不同

記錄日志『rollback 0』

進入rollback狀态

記錄日志『rollback 1』

向同步源發送一個replsetgetrbid的指令擷取一個rollbackid,這個rollbackid是用來在後面判斷在rollback過程中同步源自身是否發生復原,每個節點如果發生rollback,會修改自己的rollbackid。

記錄日志『rollback 2 findcommonpoint』

查找自己和同步源的oplog的commonpoint,這裡是從同步源最新的oplog開始逆向查找,比較自己和同步源的最新的oplog的時間,計算相差的秒數,如果超過30分鐘,那麼放棄rollback;如果本地的oplog時間戳比對方的更新,往前繼續找,直到找到時間戳相等的那條。這裡每比較一條本地的oplog,都會對oplog的内容進行解析,進而得到復原所需執行的操作集(包括需要重新從同步源擷取的文檔、需要重新同步的集合、需要drop的集合、索引等。這裡同時也會進行一些判斷,如果有發現某條oplog的大小大于512mb,放棄復原。如果有dropdatabase操作,放棄復原。)。找到了時間戳相等且hash一緻的oplog,就找到了commonpoint。

記錄日志『rollback 3 fixup』

自增rollbackid

接下來根據剛剛解析oplog得到的需要重新從同步源擷取其最新版本的文檔集,從同步源逐個擷取,并儲存在一個map中。這裡會對要復原的資料總大小進行判斷,不能超過300mb。所有文檔處理完畢後,從同步源擷取其最新的oplog的時間備用

記錄日志『rollback 3.5』

再次擷取同步源的rollbackid,如果和剛剛得到的不一樣,那說明同步源自身也發生了復原,放棄這次復原操作

記錄日志『rollback 4 n:需要更新的文檔個數』

将剛才第9步從同步源得到的最新的oplog的時間戳作為結束時間,插入一個時間戳區間到minvalid集合,表明目前資料處于不一緻狀态。

如果有需要重新同步整個集合資料或中繼資料的的,逐個處理(重新同步集合資料的,先drop然後copycollection;重新同步集合中繼資料的,擷取中繼資料并更新到本地),此處會記錄日志『rollback 4.1.1 coll resync』或『rollback 4.1.2 coll metadata resync』。這裡由于可能比較費時,記錄日志『rollback 4.2』,然後再一次擷取同步源最新的oplog時間戳記錄到minvalid集合,并再次判斷同步源是否自身發生復原。如果一切正常,記錄日志『rollback 4.3』。

記錄日志『rollback 4.6』

處理需要drop的集合(如果有),這裡會做collscan将要drop的集合的文檔的内容寫到rollback目錄裡的檔案中

處理需要drop的索引(如果有)

記錄日志『rollback 4.7』

處理剛剛從同步源擷取的最新版本的文檔集,先将本地的文檔寫到rollback目錄裡的檔案中,然後删除或更新

記錄日志『rollback 5 d:删除的文檔數 u:更新的文檔數』

記錄日志『rollback 6』

清除本地oplog集合中在commonpoint之後的oplog

reload本地的最新oplog

記錄日志『rollback done』

再次自增自己的rollbackid

記錄日志『rollback finished』