mongoDB高級篇-mongo復(fù)制集運(yùn)行原理

復(fù)制集數(shù)據(jù)同步

使用復(fù)制集的過程中,當(dāng)主節(jié)點(diǎn)有數(shù)據(jù)變更的時(shí)候,備份節(jié)點(diǎn)都會(huì)進(jìn)行數(shù)據(jù)同步操作,需要注意的是,在Mongo的復(fù)制集中,備份節(jié)點(diǎn)進(jìn)行數(shù)據(jù)同步是依賴主節(jié)點(diǎn)的oplog,oplog數(shù)據(jù)存放在主節(jié)點(diǎn)local數(shù)據(jù)庫里的一個(gè)固定集合中,每個(gè)備份節(jié)點(diǎn)自身也會(huì)維護(hù)一份自身的oplog,記錄著每一次從主節(jié)點(diǎn)同步過來的復(fù)制數(shù)據(jù)的操作。這樣,每個(gè)備份節(jié)點(diǎn)也方便提供給其他備份節(jié)點(diǎn)比較和復(fù)制使用。備份節(jié)點(diǎn)從當(dāng)前使用的數(shù)據(jù)集上執(zhí)行這些操作, 然后再將這些操作寫入自己的oplog,如果遇到故障或者同步操作失敗的情況(正常情況不會(huì)出現(xiàn),只有當(dāng)前節(jié)點(diǎn)數(shù)據(jù)丟失或者部分?jǐn)?shù)據(jù)損壞才會(huì)出現(xiàn)這樣),這個(gè)時(shí)候備份節(jié)點(diǎn)就會(huì)停止從主節(jié)點(diǎn)進(jìn)行復(fù)制數(shù)據(jù)操作。

1611046996647.png

如果說備份節(jié)點(diǎn)因?yàn)橐恍┰驅(qū)е卤紳⒒蛘吖收狭?,我們重新啟?dòng)當(dāng)前節(jié)點(diǎn)以后,會(huì)從自身維護(hù)的oplog里面的最后一條數(shù)據(jù)開始重新進(jìn)行同步,但是由于復(fù)制數(shù)據(jù)的過程,是先把數(shù)據(jù)復(fù)制過來,寫入到本地以后再去寫入oplog,那么就有可能出現(xiàn)數(shù)據(jù)同步了,oplog還沒寫入的情況,為了避免出現(xiàn)重復(fù)寫入導(dǎo)致出現(xiàn)臟數(shù)據(jù),mongo官方做了處理,保證了多次執(zhí)行oplog數(shù)據(jù)操作和一次執(zhí)行的結(jié)果是一樣的。需要注意的一點(diǎn)是,oplog的內(nèi)存大小是固定的,也就是說保存操作的數(shù)量是固定的,一般我們使用比較穩(wěn)定的話,oplog的增長速率也會(huì)比較穩(wěn)定,但是如果我們執(zhí)行了類似db.collection.remove()操作,這種情況下,如果清理了1億條文檔數(shù)據(jù),那么就會(huì)在oplog中存入1億條操作記錄,直至填滿。

初始化啟動(dòng)后同步流程

當(dāng)我們啟動(dòng)復(fù)制集以后,首先會(huì)檢查當(dāng)前狀態(tài)是否良好,并且確認(rèn)從哪一臺(tái)復(fù)制集節(jié)點(diǎn)進(jìn)行數(shù)據(jù)同步操作,當(dāng)發(fā)現(xiàn)無法從該節(jié)點(diǎn)進(jìn)行數(shù)據(jù)同步的時(shí)候,會(huì)選擇其他的節(jié)點(diǎn)嘗試進(jìn)行數(shù)據(jù)同步操作。當(dāng)確認(rèn)以后就開始了數(shù)據(jù)同步的過程:

  1. 確定了從哪一個(gè)節(jié)點(diǎn)進(jìn)行同步數(shù)據(jù)源,會(huì)在當(dāng)前的local.me中創(chuàng)建一個(gè)標(biāo)識符,并且將自身的所有用戶數(shù)據(jù)庫刪除,開始以一個(gè)全新的狀態(tài)來開始數(shù)據(jù)同步。
  2. 接著會(huì)從復(fù)制源上將所有的數(shù)據(jù)復(fù)制到本地,這個(gè)過程稱為克隆數(shù)據(jù),也是最耗時(shí)的操作
  3. 當(dāng)從復(fù)制源獲取到數(shù)據(jù)以后,就開始操作oplog了,克隆過程中所有復(fù)制來的數(shù)據(jù)的操作都會(huì)記錄到oplog中,因此需要注意,在克隆過程中,由于較為耗時(shí),可能會(huì)把復(fù)制源的數(shù)據(jù)進(jìn)行變動(dòng)或者修改,導(dǎo)致部分?jǐn)?shù)據(jù)無法克隆,這種情況只能等待下一次初始化同步才可以同步回來,因此我們在做初始化同步操作的時(shí)候需要格外注意
  4. 在oplog同步的過程中,會(huì)將oplog的每一步操作都記錄下來,直到初始化同步數(shù)據(jù)源操作完成。這個(gè)時(shí)候,理論上當(dāng)前mongo副本集的數(shù)據(jù)應(yīng)該和同步數(shù)據(jù)源的某個(gè)時(shí)間節(jié)點(diǎn)的數(shù)據(jù)完全一致,這個(gè)時(shí)候就可以創(chuàng)建數(shù)據(jù)索引了。如果說集合數(shù)據(jù)比較多,切索引數(shù)量較多,這個(gè)創(chuàng)建索引的過程就會(huì)較為耗時(shí)。
  5. 如果初始化同步完成后,發(fā)現(xiàn)當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)依然遠(yuǎn)遠(yuǎn)落后于同步的數(shù)據(jù)源的數(shù)據(jù),那么這個(gè)時(shí)候oplog同步過程中的最后一步就是將創(chuàng)建索引期間的操作記錄也同步過來,防止這個(gè)節(jié)點(diǎn)成為備份節(jié)點(diǎn)
舊數(shù)據(jù)的處理

前面我們也提到過,備份節(jié)點(diǎn)的數(shù)據(jù)有可能落后于同步源,當(dāng)落后的較多的時(shí)候,那么這個(gè)備份節(jié)點(diǎn)就是陳舊節(jié)點(diǎn),陳舊的節(jié)點(diǎn)很難跟上同步源數(shù)據(jù),如果需要繼續(xù)同步,那么勢必要跳過一部分?jǐn)?shù)據(jù)操作,當(dāng)然導(dǎo)致備份節(jié)點(diǎn)陳舊的原因有很多,例如此節(jié)點(diǎn)曾經(jīng)出現(xiàn)過停機(jī)一段時(shí)間,或者是寫入已經(jīng)超過了自身處理的能力上限,或者讀取數(shù)據(jù)的請求太多,就會(huì)導(dǎo)致備份節(jié)點(diǎn)陳舊。當(dāng)然如果某個(gè)節(jié)點(diǎn)變?yōu)殛惻f節(jié)點(diǎn)以后,會(huì)去查看當(dāng)前副本集中的其他節(jié)點(diǎn)成員,如果發(fā)現(xiàn)某個(gè)成員的oplog日志比較準(zhǔn)確,可以用于同步當(dāng)前落下的操作,就會(huì)改為從這個(gè)節(jié)點(diǎn)進(jìn)行同步,如果發(fā)現(xiàn)所有的成員節(jié)點(diǎn)都不滿足,就會(huì)停止當(dāng)前的復(fù)制操作,當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)需要再次重新完全同步(或者從備份里面恢復(fù))才可以。因此為了避免陳舊節(jié)點(diǎn)的出現(xiàn),我們往往可以考慮,優(yōu)先讓主節(jié)點(diǎn)使用比較大的oplog保存足夠多的操作日志是很好的選擇,因?yàn)橛脖P發(fā)展到現(xiàn)在已經(jīng)是成本很低的產(chǎn)品了,同時(shí)也便于其他節(jié)點(diǎn)的數(shù)據(jù)同步。

心跳

mongo復(fù)制集中為了保證成員之間彼此知道其他成員的狀態(tài),比如哪個(gè)是主節(jié)點(diǎn),哪個(gè)可以是同步源,哪個(gè)節(jié)點(diǎn)已經(jīng)不可用了?這個(gè)過程也是利用了心跳機(jī)制來維護(hù)的,默認(rèn)情況下,mongo節(jié)點(diǎn)之間每兩秒就會(huì)向其他成員節(jié)點(diǎn)發(fā)送一個(gè)心跳請求,用于通知其他節(jié)點(diǎn)當(dāng)前的狀態(tài),同時(shí)也是主節(jié)點(diǎn)是否能明確知道當(dāng)前是否滿足大多數(shù)節(jié)點(diǎn)狀態(tài)的重要依據(jù)

成員狀態(tài)

每個(gè)成員節(jié)點(diǎn)都會(huì)將當(dāng)前的狀態(tài)作為心跳的信號發(fā)送給其他成員節(jié)點(diǎn),常見的成員節(jié)點(diǎn)狀態(tài)如主節(jié)點(diǎn)、備份節(jié)點(diǎn),是我們見到最多的,但是除了這兩個(gè)以外,還有其他成員狀態(tài):

STARTUP

當(dāng)前狀態(tài)代表節(jié)點(diǎn)剛啟動(dòng)的時(shí)候的狀態(tài),處于這個(gè)狀態(tài)的時(shí)候,mongo會(huì)嘗試加載副本集相關(guān)的配置,當(dāng)加載配置完成以后,就會(huì)進(jìn)入STARTUP2狀態(tài)

STARTUP2

當(dāng)進(jìn)入到此狀態(tài)的時(shí)候,代表當(dāng)前節(jié)點(diǎn)正在進(jìn)行初始化同步操作。如果是普通的節(jié)點(diǎn),這個(gè)過程一般不會(huì)太久,幾秒鐘就能完成,這個(gè)時(shí)候mongo會(huì)創(chuàng)建幾個(gè)線程,用于完成復(fù)制和選舉等操作,完成以后狀態(tài)就會(huì)變?yōu)?code>RECOVERING狀態(tài)

RECOVERING

當(dāng)節(jié)點(diǎn)處于當(dāng)前狀態(tài)的時(shí)候,代表此節(jié)點(diǎn)運(yùn)行正常,只是暫時(shí)還沒完成,暫時(shí)無法處理請求。此狀態(tài)下,節(jié)點(diǎn)處于輕微過載,需要做一些檢查,確保當(dāng)前處于有效狀態(tài),之后就會(huì)切換到正常狀態(tài),當(dāng)然如果是陳舊節(jié)點(diǎn),也會(huì)處于這個(gè)狀態(tài),會(huì)去查找副本集中oplog較為準(zhǔn)確的成員,進(jìn)行同步oplog,完成以后也會(huì)切換到正常狀態(tài)。

ARBITER

在正常的操作中,如果節(jié)點(diǎn)屬于仲裁節(jié)點(diǎn),就應(yīng)該處于當(dāng)前狀態(tài)

DOWN

如果節(jié)點(diǎn)無法被訪問了,變?yōu)椴豢蛇_(dá)節(jié)點(diǎn),那么就會(huì)變更為此狀態(tài)。當(dāng)然不可達(dá)節(jié)點(diǎn)的原因有很多,比如有可能是網(wǎng)絡(luò)問題導(dǎo)致的訪問不到此節(jié)點(diǎn)

UNKNOWN

如果一個(gè)成員節(jié)點(diǎn)無法到達(dá)其他成員,這個(gè)時(shí)候?qū)τ谄渌蓡T來說,此節(jié)點(diǎn)的狀態(tài)就是未知的,這個(gè)時(shí)候就會(huì)將其標(biāo)記為UNKNOWN狀態(tài),標(biāo)記這個(gè)節(jié)點(diǎn)為不可達(dá)節(jié)點(diǎn)

REMOVED

當(dāng)成員退出副本集的時(shí)候,就會(huì)處于這個(gè)狀態(tài)。當(dāng)然如果重新加入副本集,此節(jié)點(diǎn)的狀態(tài)又會(huì)慢慢變?yōu)檎顟B(tài)

ROLLBACK

如果當(dāng)前節(jié)點(diǎn)正在執(zhí)行數(shù)據(jù)回滾操作,就會(huì)將狀態(tài)改為此狀態(tài),完成回滾操作以后會(huì)恢復(fù)為正常狀態(tài)

FATAL

如果節(jié)點(diǎn)發(fā)生了嚴(yán)重的錯(cuò)誤,導(dǎo)致服務(wù)不可用,此時(shí)應(yīng)該處理嘗試正?;謴?fù)服務(wù),就會(huì)將狀態(tài)標(biāo)記為FATAL,我們可以在日志中通過grep replSet FATAL關(guān)鍵字應(yīng)該可以查看到發(fā)生錯(cuò)誤的時(shí)間等信息

復(fù)制集選舉

當(dāng)一個(gè)成員節(jié)點(diǎn)加入到復(fù)制集以后,發(fā)現(xiàn)無法到達(dá)主節(jié)點(diǎn),這個(gè)時(shí)候此節(jié)點(diǎn)會(huì)認(rèn)為當(dāng)前復(fù)制集沒有主節(jié)點(diǎn),就會(huì)發(fā)起申請成為主節(jié)點(diǎn)操作,然后將請求發(fā)送給復(fù)制集中所有當(dāng)前節(jié)點(diǎn)可達(dá)的其他成員節(jié)點(diǎn)。其他成員節(jié)點(diǎn)在接受到這個(gè)請求以后,會(huì)檢查此節(jié)點(diǎn)是否滿足作為主節(jié)點(diǎn)的條件,比如先檢查在當(dāng)前復(fù)制集中是否有可達(dá)的主節(jié)點(diǎn)存在,如果已經(jīng)有主節(jié)點(diǎn)存在了,那么會(huì)直接拒絕,如果找不到已經(jīng)存在的主節(jié)點(diǎn),就會(huì)去檢查發(fā)起申請的節(jié)點(diǎn)的數(shù)據(jù)是否比當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)更新,如果比當(dāng)前數(shù)據(jù)更新,說明對于當(dāng)前節(jié)點(diǎn)來說,是比較合適作為主節(jié)點(diǎn)的,否則也會(huì)拒絕申請。

假如整個(gè)復(fù)制集中超過大半的節(jié)點(diǎn)都投了同意票,那么發(fā)出申請的節(jié)點(diǎn)就會(huì)選舉成功,成為master,切換自身的狀態(tài)到主節(jié)點(diǎn)狀態(tài),如果收到的結(jié)果是大部分反對,那么則仍然保持為備份節(jié)點(diǎn)的狀態(tài)。

當(dāng)然主節(jié)點(diǎn)被選舉出來以后,正常情況下會(huì)一直保持著主節(jié)點(diǎn)的狀態(tài),除非是心跳檢測以后,不滿足大多數(shù)可達(dá)的要求,強(qiáng)制退位,或者服務(wù)掛了導(dǎo)致退位,除此之外,我們修改了此節(jié)點(diǎn)對應(yīng)的副本集的配置,要求重新加載副本集,也會(huì)導(dǎo)致主節(jié)點(diǎn)退位。

正常網(wǎng)絡(luò)情況良好,副本集內(nèi)進(jìn)行選舉的過程是很快的,因?yàn)槟J(rèn)情況下心跳是兩秒一次,也就是說網(wǎng)絡(luò)沒有異常的情況下,兩秒內(nèi)就會(huì)有成員節(jié)點(diǎn)發(fā)現(xiàn)主節(jié)點(diǎn)狀態(tài)出現(xiàn)問題,或者出現(xiàn)了錯(cuò)誤、不可達(dá)等,這個(gè)時(shí)候就會(huì)重新進(jìn)行選舉,選舉過程很快,但是如果遇到了網(wǎng)絡(luò)響應(yīng)比較慢,甚至可能會(huì)導(dǎo)致心跳超時(shí),默認(rèn)時(shí)間為20秒,而且發(fā)起選舉可能會(huì)導(dǎo)致最終的投票平票,在平票的時(shí)候,需要等待30秒以后才能發(fā)起第二輪投票,這個(gè)時(shí)候就會(huì)等待很久,甚至好幾分鐘以上。

多機(jī)房環(huán)境下選舉回滾問題

除了正常情況下的復(fù)制集以外,有時(shí)候當(dāng)我們復(fù)制集的節(jié)點(diǎn)較多的時(shí)候,有可能出現(xiàn)在多個(gè)機(jī)房或者多個(gè)網(wǎng)絡(luò)內(nèi)組建復(fù)制集的情況,假設(shè)我們現(xiàn)在是五個(gè)節(jié)點(diǎn)組成的復(fù)制集,其中A機(jī)房是兩個(gè)節(jié)點(diǎn),B機(jī)房是三個(gè)節(jié)點(diǎn),大體如下:

1614682078533.png

此時(shí)A機(jī)房是主節(jié)點(diǎn),由于網(wǎng)絡(luò)問題,突然A機(jī)房和B機(jī)房之間無法訪問,此時(shí)對于B機(jī)房來說是主節(jié)點(diǎn)不可達(dá),而由于B機(jī)房內(nèi)還有三個(gè)節(jié)點(diǎn)存在,滿足大多數(shù)特性,這個(gè)時(shí)候,就會(huì)觸發(fā)新的主節(jié)點(diǎn)選舉操作,但是如果在網(wǎng)絡(luò)斷開的一瞬間,原本A機(jī)房的主節(jié)點(diǎn)正在處理新的請求,如圖所示,此時(shí)A機(jī)房的oplog是超過了B機(jī)房的oplog的,即在斷開之前還有部分?jǐn)?shù)據(jù)沒有復(fù)制進(jìn)B機(jī)房的備份節(jié)點(diǎn)中,這個(gè)時(shí)候B機(jī)房重新選舉出了一個(gè)master節(jié)點(diǎn),繼續(xù)對外提供服務(wù),當(dāng)操作了一段時(shí)間以后,AB機(jī)房此時(shí)網(wǎng)絡(luò)又可以訪問了,這個(gè)時(shí)候A機(jī)房的節(jié)點(diǎn)加入到復(fù)制集以后,發(fā)現(xiàn)當(dāng)前節(jié)點(diǎn)有部分oplog數(shù)據(jù)對不上,即斷開的時(shí)候多處理的那部分?jǐn)?shù)據(jù),此時(shí)就會(huì)進(jìn)入回滾操作(狀態(tài)為回滾狀態(tài)),A機(jī)房的節(jié)點(diǎn)會(huì)去B機(jī)房中找oplog共同操作的最后一條日志數(shù)據(jù),即125的oplog,這個(gè)時(shí)候會(huì)選擇回滾到125以后,在進(jìn)行同步操作。

不過mongo會(huì)去查看那些沒有被復(fù)制的操作,將這部分受影響的文檔寫入.bson文件,保存在目錄文件下的rollback目錄中,如果這個(gè)多出來的126是更新文檔的操作,會(huì)將這部分操作寫入collectionname下的bson文件中,然后再去主節(jié)點(diǎn)中復(fù)制這部分文檔數(shù)據(jù)

如果要將被回滾的操作應(yīng)用到當(dāng)前主節(jié)點(diǎn),首先使用mongorestore命令將它們加載到一個(gè)臨時(shí)集合中,然后在在shell中將這些文檔與同步后的集合進(jìn)行比較,然后在將確定的數(shù)據(jù)加載到主集合中去。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容