mongodb複製集總結

mongodb副本集

使用複製能夠將數據副本保存到多臺服務器上,即便一臺或多臺服務器出錯,也能夠保證應用程序正常運行和數據安全。
在mongodb中,建立一個副本集以後就可使用複製功能了。副本集是一組服務器,其中有一個主服務器,用於處理客戶端請求;還有多個備份服務器,用於保存主服務器的數據副本。若是主服務器崩潰了,備份服務器會自動將其中一個成員升級爲新的主服務器。mongodb

客戶端不能在備份節點執行寫操做
默認狀況下,客戶端不能從備份節點讀取數據。在備份節點上顯式地執行setSlaveOK以後,客戶端就能夠從備份節點讀取數據了。數據庫

副本集中很重要的一個概念是大多數:選擇主節點時須要由大多數決定,主節點只有在獲得大多數支持時才能繼續做爲主節點。寫操做被複制到大多數成員時這個寫操做就是安全的。這裏的大多數被定義爲副本集中一半以上的成員安全

假設一個包含5個成員的副本集,其中3個成員不可用,仍然有2個能夠正常工做,剩餘的2個成員已經沒法達到副本集大多數的要求(大可能是爲3個),因此它們沒法選舉主節點。若是這2個成員中有一個是主節點,當它注意到它沒法獲得大多數成員支持時,就會從主節點上退位。幾秒鐘以後,這個副本集中會包含2個備份節點和3個不可達節點。服務器

選舉機制

當一個備份節點沒法與主節點連通時,它就會聯繫其餘的副本集成員將本身選舉爲主節點。其餘成員會作幾項理性檢查:網絡

  • 其餘成員自身是否能和主節點連通
  • 但願被選舉爲主節點的備份節點數據是否最新
  • 有沒有其餘更高優先級的成員被選舉爲主節點

若是要求被選舉爲主節點的成員可以獲得副本集中大多數成員的投票,它就會成爲主節點。若是大多數成員中只有一個否決了本次選舉,選舉就會被取消。若是成員發現任何緣由,代表當前但願成爲主節點的成員不該該成爲主節點,那麼它就會否決這次的選舉。多線程

一張否決票至關於10000張同意票,即值爲-10000

複製操做是嚴格按時間排序的,因此候選人的最後一條操做要比它能連通的其餘全部成員更晚(或者與其餘成員相等)。架構

成員配置選項

仲裁者

不少人的應用程序使用量比較小,並不想保存三份數據副本。兩份副本已經足夠了,保存三份副本的話純粹是浪費人力,物力,財力。對於這種部署,mongodb支持一種特殊類型的成員,成爲仲裁者。仲裁者的惟一做用就是參與選舉。仲裁者並不保存數據,也不會爲客戶端提供服務,它只是爲了幫助具備兩個成員的副本集可以知足大多數這個條件。fetch

因爲仲裁者並不須要履行傳統mongod服務器的責任,因此能夠將仲裁者做爲輕量級進程,運行在配置比較差的服務器上。若是能夠,能夠將仲裁者放在單獨的故障域中,與其餘成員分開。這樣他就能夠之外部視角來看待副本集中的成員了。ui

可使用rs.addArb("$ip:$port")或在成員配置中指定arbiterOnlt選項rs.add({"_id":$id, "host":"$ip:$port", "arbiterOnly":true})線程

成員一旦以仲裁者的身份添加到副本集中,它就永遠只能是仲裁者,沒法將仲裁者從新配置爲非仲裁者,反之亦然。

使用仲裁者的另外一個好處是,若是節點擁有的節點數是偶數,仲裁者能夠投出決定勝負的關鍵一票。

優先級

優先級用於表示一個成員渴望成爲主節點的程度,取值範圍是1-100,默認是1。將優先級設爲0有特殊含義:優先級爲0的成員永遠不可以成爲主節點,這樣的成員被稱爲被動成員。擁有最高優先級的成員會優先選舉爲主節點(只要它可以獲得集合中大多數成員的同意票,而且數據是最新的)

隱藏成員

客戶端不會向隱藏成員發送請求,隱藏成員也不會做爲複製源。所以,不少人會將不夠強大的服務器或備份服務器隱藏起來。只有優先級爲0的成員才能被隱藏。即設置priority=0hidden=true

延遲備份節點

數據可能會由於人爲錯誤而遭受毀滅性的破壞,爲了防止這類問題,可使用slaveDelay設置一個延遲的備份節點。
延遲備份節點會比主節點延遲指定的時間(秒),這是有意爲之的。這樣,若是有人不當心摧毀了你的主集合,還能夠將數據從先前的備份中恢復過來。slaveDelay要求成員的優先級是0,若是應用會將讀請求路由到備份節點,應該將延遲備份節點隱藏掉,以避免讀請求被路由到延遲備份節點。

不建立索引的成員

有時備份節點並不須要與主節點擁有相同的索引,甚至能夠沒有索引。若是某個備份節點的用途僅僅是處理數據備份或者離線的批量任務,那麼你可能但願在它的成員配置中指定"buildIndexes":false。這個選項能夠阻止備份節點建立索引。這是一個永久選項,這個成員將永遠沒法恢復爲能夠建立索引的成員。要求成員的優先級爲0

同步的過程

mongodb的複製功能是使用操做日誌oplog實現的,操做日誌包含了主節點的每一次寫操做。oplog是主節點的local數據庫中的一個固定集合。備份節點經過查詢這個集合就能夠知道須要進行復制的操做。

每一個備份節點都維護着本身的oplog,記錄着每一次從主節點複製數據的操做。這樣,每一個成員均可以做爲同步源提供給其餘成員使用。

備份節點從當前使用的同步源中獲取須要執行的操做,而後在本身的數據集上執行這些操做,最後再將這些操做寫入本身的oplog。若是某個備份節點掛掉了,當它從新啓動後,就會自動從oplog中最後一個操做開始進行同步。因爲複製操做的過程是先複製數據再寫入oplog,因此備份節點可能會在已經同步過的數據上再次執行復制操做。mongodb在設計之初就考慮到了這種狀況:將oplog的同一個操做執行屢次與只執行一次的效果是同樣的。

因爲oplog的大小是固定的,它只能保存特定數量的操做日誌。若是單個操做會影響多個文檔,例如db.coll.remove(),刪除了10000個文檔,那麼oplog中就會有10000條操做日誌,每條日誌對應一個被刪除的文檔。若是執行大量的批量操做,oplog很快就會被填滿。

選擇同步源

MongoDB 默認是採起級聯複製的架構,就是默認不必定選擇主庫做爲本身的同步源,若是不想讓其進行級聯複製,能夠經過 chainingAllowed 參數來進行控制。在級聯複製的狀況下,你也能夠經過 replSetSyncFrom 命令來指定你想複製的同步源。因此這裏說的同步源其實相對於從庫來講就是它的主庫。那麼同步源的選取流程是怎樣的呢?
MongoDB 從庫會在副本集其餘節點經過如下條件篩選符合本身的同步源。

  • 若是設置了 chainingAllowed 爲 false,那麼只能選取主庫爲同步源
  • 找到與本身 ping 時間最小的而且數據比本身新的節點(在副本集初始化的時候,或者新節點加入副本集的時候,新節點對副本集的其餘節點至少 ping 兩次)
  • 該同步源與主庫最新 optime 作對比,若是延遲主庫超過 30s,則不選擇該同步源。

在第一次的過濾中,首先會淘汰比本身數據還舊的節點。若是第一次沒有,那麼第二次須要算上這些節點,防止最後沒有節點能夠作爲同步源了。最後確認該節點是否被禁止參與選舉,若是是則跳過該節點。經過上述篩選最後過濾出來的節點做爲新的同步源。

其實 MongoDB 同步源在除了在 Initial Sync 和增量複製 的時候選定以後呢,並非一直是穩定的,它可能在如下狀況下進行變動同步源:

  • ping 不通本身的同步源
  • 本身的同步源角色發生變化
  • 本身的同步源與副本集任意一個節點延遲超過 30s

刪除除local之外的全部數據庫

全量同步

  1. 在建立的集合的時候同時建立了索引(與主庫同樣),在 MongoDB 3.4 版本以前只建立 _id 索引,其餘索引等待數據 copy 完成以後進行建立。
  2. 在建立集合和拷貝數據的同時,也將 oplog 拷貝到本地 local 數據庫中,等到數據拷貝完成以後,開始應用本地 oplog 數據。

克隆數據是耗時操做,若是克隆完成後,新成員數據同步速度趕不上同步源的變化速度,同步源可能會將新成員須要複製的某些數據oplog覆蓋掉。

備份節點遠遠落後於同步源當前的操做,那麼這個備份節點就是陳舊的。
當一個備份節點陳舊以後,它會查看副本集中的其餘成員,若是某個成員的oplog足夠詳盡,能夠用於處理那些落下的操做,就從這個成員處進行同步。若是任何一個成員的oplog都沒有參考價值,那麼這個成員上的複製操做就會停止,這個成員須要從新進行全量同步。

增量同步

  1. Sencondary 初始化同步完成以後,開始增量複製,經過 produce 線程在 Primary oplog.rs 集合上創建 cursor,而且實時請求獲取數據。
  2. Primary 返回 oplog 數據給 Secondary。
  3. Sencondary 讀取到 Primary 發送過來的 oplog,將其寫入到隊列中。
  4. Sencondary 的同步線程會經過 tryPopAndWaitForMore 方法一直消費隊列,當每次達到必定的條件以後,條件以下:

    • 總數據大於 100MB
    • 已經取到部分數據但沒到 100MB,可是目前隊列沒數據了,這個時候會阻塞等待一秒,若是尚未數據則本次取數據完成。

上述兩個條件知足一個以後,就會將數據給 prefetchOps 方法處理,prefetchOps 方法主要將數據以 database 級別切分,便於後面多線程寫入到數據庫中。若是採用的 WiredTiger 引擎,那這裏是以 Docment ID 進行切分。

  1. 最終將劃分好的數據以多線程的方式批量寫入到數據庫中(在從庫批量寫入數據的時候 MongoDB 會阻塞全部的讀)。
  2. 而後再將 Queue 中的 Oplog 數據寫入到 Sencondary 中的 oplog.rs 集合中。

心跳

每一個成員須要知道其餘成員的狀態:哪一個是主節點?哪一個能夠做爲同步源?哪一個掛掉了?爲了維護集合的最新視圖,每一個成員每隔2秒就會向其餘成員發送一個心跳請求。心跳請求的信息量很小,用於檢查每一個成員的狀態。
心跳最重要的功能之一就是讓主節點知道本身是否知足集合大多數的條件。若是主節點再也不獲得大多數服務器的支持,它就會退位,變成備份節點。

成員狀態

各個成員會經過心跳將本身當前的狀態告訴其餘成員。

  • STARTUP

成員剛啓動時處於這個狀態,在這個狀態下,mongodb會嘗試加載成員的副本集配置。配置加載成功後,就進入STARTUP2狀態。

  • STARTUP2

整個初始化同步過程都處於這個狀態。當初始化同步完成後,進入RECOVERING狀態。

  • RECOVERING

這個狀態代表成員運轉正常,可是暫時還不能處理讀取請求。在處理很是耗時的操做時,成員也可能進入RECOVERING狀態,好比壓縮或replSetMaintenance命令。當一個成員和其餘成員脫節時,也會進入這個狀態,一般來講,這時這個成員處於無效狀態,須要從新同步。可是,成員這時並無進入錯誤狀態,由於它指望一個擁有足夠詳盡oplog的成員,而後繼續同步oplog,而後回到正常狀態。

  • ARBITER

仲裁者狀態。

  • DOWN

不可達的成員會被報告爲DOWN狀態。它有可能仍然是運行狀態,只是網絡不可達。

  • UNKNOWN

別的成員暫時不知道該成員的狀態,會被報告爲該狀態

  • REMOVED

成員被移除副本集時的狀態

  • ROLLBACK

正在進行數據回滾的狀態,回滾完成後會轉換爲RECOVERING狀態

  • FATAL

一個成員發生了不可挽回的錯誤,也再也不嘗試恢復正常。應該查看詳細日誌查明爲什麼該成員處於FATAL狀態。一般應該重啓服務器,進行從新同步或者從備份中恢復

  • PRIMARY

主節點狀態

  • SECONDARY

備份節點狀態

選舉

當一個節點沒法到達主節點時,它就會申請被選舉爲主節點。但願被選舉爲主節點的成員會向它能到達的全部成員發通知。若是這個成員不符合候選人的要求:

  1. 這個成員的數據落後於副本集
  2. 有一個運行中的主節點(那個力求被選舉爲主節點的成員沒法到達這個主節點)

在這些狀況下,其餘成員不會容許進行選舉。

加入沒有反對的理由,其餘成員就會對這個成員進行選舉投票。若是這個成員獲得副本集中大多數成員的同意票,它就會選舉成功,會轉換到主節點狀態。若是達不到大多數成員的要求,就會選舉失敗,仍然處於備份節點的狀態。以後還能夠再次申請被選舉爲主節點。主節點若再也不知足大多數節點的要求,會退位。

心跳超時時間爲20秒,若選舉達成平局,每一個成員須要等待30秒才能開始下一次選舉。

回滾

咱們知道在發生切換的時候是有可能形成數據丟失的,主要是由於主庫宕機,可是新寫入的數據尚未來得及同步到從庫中,這個時候就會發生數據丟失的狀況。
那針對這種狀況,MongoDB 增長了回滾的機制。在主庫恢復後從新加入到複製集中,這個時候老主庫會與同步源對比 oplog 信息,這時候分爲如下兩種狀況:

  1. 在同步源中沒有找到比老主庫新的 oplog 信息。
  2. 同步源最新一條 oplog 信息跟老主庫的 optime 和 oplog 的 hash 內容不一樣。

針對上述兩種狀況 MongoDB 會進行回滾,回滾的過程就是逆向對比 oplog 的信息,直到在老主庫和同步源中找到對應的 oplog,而後將這期間老主庫的 oplog 所有記錄到 rollback 目錄裏的文件中,並將這期間的oplog撤銷。

以後能夠將這些被回滾的操做應用到當前的主節點。

如下狀況會終止回滾:

  1. 對比老主庫的 optime 和同步源的 optime,若是超過了 30 分鐘,那麼放棄回滾。
  2. 在回滾的過程當中,若是發現單條 oplog 超過 512M,則放棄回滾。
  3. 若是有 dropDatabase 操做,則放棄回滾。
  4. 最終生成的回滾記錄超過 300M,也會放棄回滾。

這種狀況最多見的緣由是備份節點遠遠落後於主節點,而這時主節點掛了。若是其中一個備份節點成爲主節點,這個主節點與舊的主節點相比,缺乏不少操做。爲了保證成員不會在備份中失敗,最好的方式是保持備份節點的數據儘量的最新。

參考文章

https://www.infoq.cn/article/...《mongodb權威指南》

相關文章
相關標籤/搜索