回滾(rollback)操作是MongoDB副本集發生一些異常主備切換后可能發生的現象;貪L操作會撤銷在當前節點上已執行的一些修改操作。
什么時候會觸發回滾
MongoDB副本集節點上有個同步線程,負責拉取需要同步的oplog。被拉取oplog的節點稱作同步源。那么,要回滾,首先要有一個同步源。
同步源
鏈式復制
平時我們都說主備同步主備同步,那同步源肯定是主節點了?其實不一定,MongoDB很早就支持了鏈式復制,即備節點可以從另外一個備節點拉取oplog,而不只從主節點拉取。這樣一來可以減少主節點的負載,二來各節點可以選擇離自己近的節點進行同步。當然,在某些情況下,這可能會導致一些備節點的延遲變大。鏈式復制可以通過以下命令來打開或關閉:
cfg = rs.config()
cfg.settings.chainingAllowed = true/false
rs.reconfig(cfg)
Secondary節點如何選擇同步源
Secondary節點會根據以下原則選擇一個同步源:
如果之前有通過命令replSetSyncFrom指定了同步源,那么使用此同步源
由于后續需要根據到其他節點的ping值(通過心跳進行統計)信息進行選擇,這里會判斷一下是否已有足夠的信息,需要等待更多的心跳包,如果不需要,繼續,否則直接返回,等下次需要選擇時再看
如果沒有開啟Chained Replication(鏈式復制),那么選擇Primary
通過兩輪選擇,基于以下規則選擇一個ping值最低的節點:
如果自己可以建索引,那么只能從同樣可以建索引的節點同步
oplog的時間戳比我新(這里是獲取該節點上次心跳包里帶的appliedOpTime的時間戳進行比較)
不在黑名單中(注:何時將同步節點加進黑名單?1. 連接不上該節點,加10s黑名單;2. 落后該節點太多無法繼續同步,加1min黑名單)
其中在第一輪選擇中,會額外考慮以下條件:
1. 擁有投票權的節點只能從同樣擁有投票權的節點同步
2. 不能從hidden節點同步
3. 不能從落后Primary太多(超過配置的maxSyncSourceLagSecs)的節點同步
4. 不能從配置了比自己擁有更大delay的節點同步
如果第一輪沒有選出合適的節點,那么再進行第二輪選擇,放寬上述條件的限制。
什么時候會觸發回滾(續)
回到回滾觸發條件。同步線程已經選擇出了一個同步源,它向同步源發起一個find請求,查詢大于等于其最新的oplog時間戳的oplog。如果發生以下兩種情況,那么需要回滾:
1. 在同步源上沒有查到比其更新的oplog(我們剛剛通過一系列麻煩的規則選出它作為同步源,但是我們的oplog卻比它還新)
2. 返回的的第一條oplog和其最新的oplog的OpTime和hash都不同,注意這里是比較整個OpTime,即除了時間戳之外還包括term,首先會比較term,如果term不同,那就不同
回滾具體流程
回滾之前會獲取minvalid集合的數據進行判斷當前節點是否處于一致的狀態,如果不是則直接assert結束進程。關于minvalid集合的作用,可參見MongoDB中local.replset.minvalid集合的作用。如果允許進行回滾,則執行以下步驟:
記錄日志『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』
3.2.11以前回滾的bug
需要注意的是,當執行完最后一步記錄日志『rollback finished』,其實回滾還沒真正結束。此時節點會進入RECOVERING狀態,minvalid集合中還記錄了一個時間戳區間。即節點在回滾過程中記錄了需要同步到哪個OpTime,后續等同步線程追上這個時間點后才能變成SECONDARY狀態。如果在這時候,發生了同步源切換,比如切換到另外一個同樣需要回滾的節點,并且又將剛剛已清除掉的commonPoint之后的oplog給同步回來,那么就可能觸發第二次回滾觸發assert退出。關于這個bug,MongoDB官方已在3.2.11版本中修復SERVER-25145,修復方法是在選擇同步源的時候增加是否包含minvalid中OpTime的判斷。
轉載請注明: 文章轉載自:愛思資源網 http://www.randomwithlife.com/show-72-915-1.html