資料複製原理

  開啟複製集後,主節點會在local庫下生成一個集合叫 oplog.rs,這是一個有限的集合,即大小固定。這個集合記入了整個mongod例項一段時間內資料庫的所有變更操作(如:增/刪/改),當空間用完時新的記入會覆蓋最老的記錄。而複製集的從節點就是通過讀取主節點上面的oplog來實現資料同步的。oplog.rs的滾動覆蓋寫入有兩種方式:一種是達到設定大小就開始覆蓋寫入;二是設定文件數量,達到文件數量就開始覆蓋寫入(不推薦使用)。

下圖為複製集的工作方式:

  主節點和應用程式之間的互動是通過Mongodb驅動進行的,MongoDB複製集有自動故障轉移功能,我們可以通過rs.isMaster()函式來識別誰是主節點。預設應用程式的讀寫都是在主節點上,預設情況下,讀寫都只能在主節點上進行。但是主壓力過大時就可以把讀操作分離到從節點從而提高效能。下面是MongoDB的驅動支援5種複製集讀選項:

  primary:預設模式,所有的讀操作都在複製集的主節點進行的。

  primaryPreferred:在大多數情況時,讀操作在主節點上進行,但是如果主節點不可用了,讀操作就會轉移到從節點上執行。

  secondary:所有的讀操作都在複製集的從節點上執行。

  secondaryPreferred:在大多數情況下,讀操作都是在從節點上進行的,但是當從節點不可用了,讀操作會轉移到主節點上進行。

  nearest:讀操作會在複製集中網路延時最小的節點上進行,與節點型別無關。

但是除了primary 模式以外的複製集讀選項都有可能返回非最新的資料,因為複製過程是非同步的,從節點上應用操作可能會比主節點有所延後。如果我們不使用primary模式,請確保業務允許資料存在可能的不一致。

複製集寫操作

如果啟用複製集的話,就在記憶體中會多一個oplog區域,是節點之間進行同步資料的手段,它會把操作日誌放到oplog中,然後oplog會複製到從節點上,從節點接收並執行oplog上的操作記入,從而達到同步的效果:

步驟:

  1.客戶端的資料進來

  2.資料操作寫入日誌緩衝

  3.資料寫入到資料緩衝

  4.把日誌緩衝中的操作放到oplog中

  5.返回操作結果到客戶端

  6. 後臺執行緒進行OPLOG複製到從節點,這個頻率是非常高的,比日誌刷盤頻率還要高,從節點會一直監聽主節點,OPLOG一有變化就會進行復制操作

  7. 後臺執行緒進行日誌緩衝中的資料刷盤,非常頻繁(預設100)毫秒,也可自行設定(30-60);

需要重點強調的是oplog只記錄改變資料庫狀態的操作。比如,查詢就不儲存在oplog中。這是因為oplog只是作為從節點與主節點保持資料同步的機制。儲存在oplog中的操作也不是完全和主節點的操作一模一樣的,這些操作在儲存之前先要做等冪變換,也就是說,這些操作可以在從伺服器端多次執行,只要順序是對的,就不會有問題。例如,使用“$inc”執行的增加更新操作,會被轉換為“$set”操作。

如下圖表示流程:

oplog大小

  當你第一次啟動複製集中的節點時,MongoDB會用預設大小建立Oplog。這個預設大小取決於你的機器的作業系統。大多數情況下,預設的oplog大小是足夠的。在 mongod 建立oplog之前,我們可以通過設定 oplogSizeMB 選項來設定其大小。但是,如果已經初始化過複製集,已經建立了Oplog了,我們需要通過修改Oplog大小中的方式來修改其大小。

OpLog的預設大小

  • 在64位Linux、Windows作業系統上為當前分割槽可用空間的5%,但最大不會超過50G。
  • 在64位的OS X系統中,MongoDB預設分片183M大小給Oplog。
  • 在32位的系統中,MongoDB分片48MB的空間給Oplog。

複製時間視窗:

  既然Oplog是一個封頂集合,那麼Oplog的大小就會有一個複製時間視窗的問題。舉個例子,如果Oplog是大小是可用空間的5%,且可以儲存24小時內的操作,那麼從節點就可以在停止複製24小時後仍能追趕上主節點,而不需要重新獲取全部資料。如果說從節點在24小時後開始追趕資料,那麼不好意思主節點的oplog已經滾動覆蓋了,把從節點沒有執行的那條語句給覆蓋了。這個時候為了保證資料一致性就會終止複製。然而,大多數複製集中的操作沒有那麼頻繁,oplog可以存放遠不止上述的時間的操作記錄。但是,再生產環境中儘可能把oplog設定大一些也不礙事。使用rs.printReplicationInfo()可以檢視oplog大小以及預計視窗覆蓋時間。

 Oplog大小應隨著實際使用壓力而增加

如果我能夠對我複製集的工作情況有一個很好地預估,如果可能會出現以下的情況,那麼我們就可能需要建立一個比預設大小更大的oplog。相反的,如果我們的應用主要是讀,而寫操作很少,那麼一個小一點的oplog就足夠了。

下列情況我們可能需要更大的oplog。

同時更新大量的文件

Oplog為了保證 冪等性 會將多項更新(multi-updates)轉換為一條條單條的操作記錄。這就會在資料沒有那麼多變動的情況下大量的佔用oplog空間。

刪除了與插入時相同大小的資料

如果我們刪除了與我們插入時同樣多的資料,資料庫將不會在硬碟使用情況上有顯著提升,但是oplog的增長情況會顯著提升。

大量In-Place更新

如果我們會有大量的in-place更新,資料庫會記錄下大量的操作記錄,但此時硬碟中資料量不會有所變化。

Oplog冪等性

Oplog有一個非常重要的特性——冪等性(idempotent)。即對一個數據集合,使用oplog中記錄的操作重放時,無論被重放多少次,其結果會是一樣的。舉例來說,如果oplog中記錄的是一個插入操作,並不會因為你重放了兩次,資料庫中就得到兩條相同的記錄。

複製集資料同步過程

  先通過initial sync同步全量資料,再通過replication不斷重放Primary上的oplog同步增量資料。初始同步會將完整的資料集複製到各個節點上,Secondary啟動後,如果滿足以下條件之一,會先進行initial sync。

1. Secondary上oplog為空,比如新加入的空節點。

2. local.replset.minvalid集合裡_initialSyncFlag標記被設定。當initial sync開始時,同步執行緒會設定該標記,當initial sync結束時清除該標記,故如果initial sync過程中途失敗,節點重啟後發現該標記被設定,就知道應該重新進行initial sync。

3. BackgroundSync::_initialSyncRequestedFlag被設定。當向節點發送resync命令時,該標記會被設定,此時會強制重新initial sync。

initial sync同步流程

1.   minValid集合設定_initialSyncFlag(db.replset.minvalid.find())。

2.   獲取同步源當前最新的oplog時間戳t0。

3.   從同步源克隆所有的集合資料。

4.   獲取同步源最新的oplog時間戳t1。

5.   同步t0~t1所有的oplog。

6.   獲取同步源最新的oplog時間戳t2。

7.  同步t1~t2所有的oplog。

8.   從同步源讀取index資訊,並建立索引(除了_id ,這個之前已經建立完成)。

9.   獲取同步源最新的oplog時間戳t3。

10.   同步t2~t3所有的oplog。

11.    minValid集合清除_initialSyncFlag,initial sync結束。

當完成了所有操作後,該節點將會變為正常的狀態secondary。

Mongodb複製集的搭建

主節點(Primary)

在複製集中,主節點是唯一能夠接收寫請求的節點。MongoDB在主節點進行寫操作,並將這些操作記錄到主節點的oplog中。而從節點將會從oplog複製到其本機,並將這些操作應用到自己的資料集上。(複製集最多隻能擁有一個主節點)

從節點(Secondaries)

從節點通過應用主節點傳來的資料變動操作來保持其資料集與主節點一致。從節點也可以通過增加額外引數配置來對應特殊需求。例如,從節點可以是non-voting或是priority 0.

仲裁節點(ARBITER)

仲裁節點即投票節點,其本身並不包含資料集,且也無法晉升為主節點。但是,旦當前的主節點不可用時,投票節點就會參與到新的主節點選舉的投票中。仲裁節點使用最小的資源並且不要求硬體裝置。投票節點的存在使得複製集可以以偶數個節點存在,而無需為複製集再新增節點 不要將投票節點執行在複製集的主節點或從節點機器上。 投票節點與其他 複製集節點的交流僅有:選舉過程中的投票,心跳檢測和配置資料。這些互動都是不加密的。

注意:

  仲裁節點(Arbiter)是複製集中的一個mongodb例項,它並不儲存資料。仲裁節點使用最小的資源並且不要求硬體裝置,不能將Arbiter部署在同一個資料集節點中,可以部署在其他應用伺服器或者監視伺服器中,也可部署在單獨的虛擬機器中。為了確保複製集中有奇數的投票成員(包括primary),需要新增仲裁節點做為投票,否則primary不能執行時不會自動切換primary。如下圖所示;

配置檔案準備

先為每一個節點建立資料目錄和日誌目錄

  1. [root@hdp4 ~]# mkdir mongodb1
  2. [root@hdp4 ~]# mkdir mongodb2
  3. [root@hdp4 ~]# mkdir mongodb3
  1. [root@hdp4 ~]# mkdir mongodblog

建立配置檔案:

每個節點都建立一個配置檔案:

  1. vim /usr/local/mongodb/mongodb1.conf
  1. bind_ip=192.168.203.114
  2. port=
  3. dbpath=/root/mongodb1/ // 上面所創的資料目錄
  4. logpath=/root/mongodb/mongodb1.log // 再日誌目錄上建立日誌檔案
  5. logappend=true
  6. fork=true
  7. maxConns=
  8. replSet=haha // 複製集名稱所有節點需要一樣
  1. vim /usr/local/mongodb/mongodb2.conf
  1. bind_ip=192.168.203.114
  2. port=
  3. dbpath=/root/mongodb2/
  4. logpath=/root/mongodb/mongodb2.log
  5. logappend=true
  6. fork=true
  7. maxConns=
  8. replSet=haha // 複製集名稱所有節點需要一樣
  1. vim /usr/local/mongodb/mongodb3.conf
  1. bind_ip=192.168.203.114
  2. port=
  3. dbpath=/root/mongodb3/
  4. logpath=/root/mongodb/mongodb3.log
  5. logappend=true
  6. fork=true
  7. maxConns=
  8. replSet=haha // 複製集名稱所有節點需要一樣

啟動mongod

用配置檔案啟動所有節點:

  1. mongod -f /usr/local/mongodb/mongodb1.conf
  2. mongod -f /usr/local/mongodb/mongodb2.conf
  3. mongod -f /usr/local/mongodb/mongodb3.conf

初始化主節點:先用客戶端進行連線

  1. mongo --host 192.168.203.114 --port
  1. >use admin;

  初始化配置:

  1. >config = {
  2. _id: 'haha',
  3. members: [
  4. {_id: , host: '192.168.203.114:28017'},
  5. {_id: , host: '192.168.203.114:28018'},
  6. {_id: , host: '192.168.203.114:28019'}
  7. ]
  8. }
  1. >rs.initiate(config)

新增從節點

  1. haha:PRIMARY>rs.add('192.168.203.114:27018');

新增仲裁節點

  1. haha:PRIMARY> rs.addArb('192.168.203.114:27019');

檢視狀態

  1. haha:PRIMARY> rs.status();
  1. {
  2. "set" : "haha",
  3. "date" : ISODate("2019-03-13T12:34:14.813Z"),
  4. "myState" : ,
  5. "term" : NumberLong(),
  6. "syncingTo" : "",
  7. "syncSourceHost" : "",
  8. "syncSourceId" : -,
  9. "heartbeatIntervalMillis" : NumberLong(),
  10. "optimes" : {
  11. "lastCommittedOpTime" : {
  12. "ts" : Timestamp(, ),
  13. "t" : NumberLong()
  14. },
  15. "appliedOpTime" : {
  16. "ts" : Timestamp(, ),
  17. "t" : NumberLong()
  18. },
  19. "durableOpTime" : {
  20. "ts" : Timestamp(, ),
  21. "t" : NumberLong()
  22. }
  23. },
  24. "members" : [
  25. {
  26. "_id" : ,
  27. "name" : "192.168.203.114:27017",
  28. "health" : ,
  29. "state" : ,
  30. "stateStr" : "PRIMARY",
  31. "uptime" : ,
  32. "optime" : {
  33. "ts" : Timestamp(, ),
  34. "t" : NumberLong()
  35. },
  36. "optimeDate" : ISODate("2019-03-13T12:34:06Z"),
  37. "syncingTo" : "",
  38. "syncSourceHost" : "",
  39. "syncSourceId" : -,
  40. "infoMessage" : "",
  41. "electionTime" : Timestamp(, ),
  42. "electionDate" : ISODate("2019-03-13T11:35:45Z"),
  43. "configVersion" : ,
  44. "self" : true,
  45. "lastHeartbeatMessage" : ""
  46. },
  47. {
  48. "_id" : ,
  49. "name" : "192.168.203.114:27018",
  50. "health" : ,
  51. "state" : ,
  52. "stateStr" : "SECONDARY",
  53. "uptime" : ,
  54. "optime" : {
  55. "ts" : Timestamp(, ),
  56. "t" : NumberLong()
  57. },
  58. "optimeDurable" : {
  59. "ts" : Timestamp(, ),
  60. "t" : NumberLong()
  61. },
  62. "optimeDate" : ISODate("2019-03-13T12:34:06Z"),
  63. "optimeDurableDate" : ISODate("2019-03-13T12:34:06Z"),
  64. "lastHeartbeat" : ISODate("2019-03-13T12:34:14.366Z"),
  65. "lastHeartbeatRecv" : ISODate("2019-03-13T12:34:13.931Z"),
  66. "pingMs" : NumberLong(),
  67. "lastHeartbeatMessage" : "",
  68. "syncingTo" : "192.168.203.114:27017",
  69. "syncSourceHost" : "192.168.203.114:27017",
  70. "syncSourceId" : ,
  71. "infoMessage" : "",
  72. "configVersion" :
  73. },
  74. {
  75. "_id" : ,
  76. "name" : "192.168.203.114:27019",
  77. "health" : ,
  78. "state" : ,
  79. "stateStr" : "ARBITER",
  80. "uptime" : ,
  81. "lastHeartbeat" : ISODate("2019-03-13T12:34:14.366Z"),
  82. "lastHeartbeatRecv" : ISODate("2019-03-13T12:34:12.498Z"),
  83. "pingMs" : NumberLong(),
  84. "lastHeartbeatMessage" : "",
  85. "syncingTo" : "",
  86. "syncSourceHost" : "",
  87. "syncSourceId" : -,
  88. "infoMessage" : "",
  89. "configVersion" :
  90. },
  91.  
  92. ],
  93. "ok" :
  94. }

以上就是Mongodb的複製集安裝搭建。

注意:本片文章的前半部分原理是來源於:https://www.cnblogs.com/ExMan/p/9665060.html  筆者在這個基礎上加上了搭建部分