MongoDB副本集配置和資料遷移實戰
MongoDB副本集配置和資料遷移實戰
環境:Ubuntu 16.04, MongoDB 3.6
基本概念
MongoDB 的副本集就是有自動故障恢復功能的 MongoDB 主從叢集。由於 MongoDB 的主從複製功能不支援高可用,所以從 3.2 版本開始已經被廢棄了,轉而用副本集來代替。一個副本集總會有一個活躍節點(Primary)和若干個備份節點(Secondary),還有一個可選的一個仲裁者(Arbiter)節點來實現HA中的故障切換。
準備工作
- 準備三臺伺服器(虛擬機器亦可),系統全部安裝Ubuntu 16.04 例如:
- Primary 192.168.10.58
- Secondary 192.168.10.59
- Arbiter 192.168.10.57
- 三臺伺服器上都安裝好 MongoDB 3.6
參考官方的安裝文件 https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
配置副本集 Primary 和 Secondary 節點
- 建立資料目錄
$ mkdir -p /mnt/mongodb/replset
- 啟動名為“my-repl”的副本集,埠為27017,繫結到任意IP(也可以指定IP)
$ mongod --dbpath /mnt/mongodb/replset --port 27017 --replSet "my-repl" --bind_ip_all
- 初始化副本集
- 用mongo客戶端連線 Primary 節點:
$ mongo
- 執行初始化指令碼來建立副本集:
輸出結果:> rs.initiate({ _id:"my-repl", members:[ {_id:0, host:"192.168.10.58:27017"}, {_id:1, host:"192.168.10.59:27017"} ] });
{ "ok" : 1, "operationTime" : Timestamp(1523237919, 1), "$clusterTime" : { "clusterTime" : Timestamp(1523237919, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
- 檢視配置結果
配置過程非常簡單,可以看到在副本整合員中有0和1兩個節點,此時主從兩個伺服器已經可以工作了,伺服器0(Primary)有任何資料的變化都會同步到伺服器1(Secondary)上。但是,此時的副本集只是提供了資料備份的功能,並不能達到高可用。如果要達到這一點,那麼需要配置一個仲裁者節點(Arbiter)> rs.conf(); { "_id" : "my-repl", "version" : 1, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "192.168.10.58:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.10.59:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5acac41fded47067da446ddd") } }
配置仲裁者(Arbiter)
仲裁者在 Primary 節點發生故障時,參與副本集的選舉投票決定哪個副本成為 Primary 節點。仲裁節點不儲存資料也不會成為 Primary 節點。
-
仲裁者通常不部署在大磁碟空間的伺服器上,因此為了最小化預設建立資料,修改配置:
$ vim /etc/mongod.conf
storage.journal.enabled=false storage.mmapv1.smallFiles = true.
-
建立仲裁者目錄並啟動服務
$ mkdir /mnt/mongodb/arbiter $ mongod --port 27017 --dbpath /mnt/mongodb/arbiter --replSet 'my-repl' --bind_ip_all
-
把仲裁者新增到副本集中
-
連線至 Primary 伺服器
$mongo --host 192.168.10.58
my-repl:PRIMARY> rs.addArb("192.168.10.57:27017") { "ok" : 1, "operationTime" : Timestamp(1523326877, 1), "$clusterTime" : { "clusterTime" : Timestamp(1523326877, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
-
-
檢視副本集的效果:
>rs.status(); my-repl:PRIMARY> rs.status(); { "set" : "my-repl", "date" : ISODate("2018-04-10T02:21:44.826Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "appliedOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "192.168.10.58:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 2891, "optime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-04-10T02:21:35Z"), "electionTime" : Timestamp(1523324284, 1), "electionDate" : ISODate("2018-04-10T01:38:04Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "192.168.10.59:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 2624, "optime" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1523326895, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-04-10T02:21:35Z"), "optimeDurableDate" : ISODate("2018-04-10T02:21:35Z"), "lastHeartbeat" : ISODate("2018-04-10T02:21:43.080Z"), "lastHeartbeatRecv" : ISODate("2018-04-10T02:21:43.083Z"), "pingMs" : NumberLong(0), "syncingTo" : "192.168.10.58:27017", "configVersion" : 2 }, { "_id" : 2, "name" : "192.168.10.57:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 27, "lastHeartbeat" : ISODate("2018-04-10T02:21:43.079Z"), "lastHeartbeatRecv" : ISODate("2018-04-10T02:21:42.088Z"), "pingMs" : NumberLong(0), "configVersion" : 2 } ], "ok" : 1, "operationTime" : Timestamp(1523326895, 1), "$clusterTime" : { "clusterTime" : Timestamp(1523326895, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
可以看到狀態顯示:伺服器0為 Primary, 伺服器1為 Secondary, 伺服器2為 Arbiter。
此時帶有高可用的副本集已經配置完成,Arbiter 會監控 Primary 節點的執行情況,如果伺服器0發生了宕機,那麼仲裁者 Arbiter 節點會發起選舉,最終選取多個 Secondary 中的某一個來作為 Primary 節點。我們測試的架構中只有一個 Secondary 節點,那麼此伺服器1就會成為 Primary。而當伺服器0恢復工作時,它會被當成 Secondary 來執行。 -
副本優先順序
副本集會在 Primary 節點出現故障時把 Secondary 提升為 Primary,但是很多情況下 Secondary 只是作為備用節點,我們不希望它長期作為 Primary 節點執行。那麼為了達到這個目的,我們修改副本的優先順序。- 連線 Primary 節點,執行以下指令碼:
cfg = rs.conf() cfg.members[0].priority = 10 cfg.members[1].priority = 5 rs.reconfig(cfg) { "ok" : 1, "operationTime" : Timestamp(1523411797, 2), "$clusterTime" : { "clusterTime" : Timestamp(1523411797, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
- 如果在非 Prmiary 上比如Arbiter上執行,會報下面的錯誤:
{ "ok" : 0, "errmsg" : "replSetReconfig should only be run on PRIMARY, but my state is ARBITER; use the \"force\" argument to override", "code" : 10107, "codeName" : "NotMaster" }
- 以上操作讓伺服器0的優先順序高於伺服器1,那麼當伺服器0從故障中恢復時,它會重新成為 Primary 節點來提供服務。
資料遷移
在配置副本集之前,你可能已經存在了一個單獨的 MongoDB 例項儲存了一些資料,你需要把原來例項中的資料遷移到新的副本集中。(你也可以一開始就把原來的單個例項配置成副本集的 Primary 節點,然後新增副本來同步資料,此方法不在本文討論範圍之內)
-
登入原例項所在的伺服器,匯出資料:
$ mongoexport -h localhost -p 27017 -u xxx -p xxx -d MY_DB_NAME -c MY_COLLECTION_NAME -o MY_COLLECTION_NAME.dmp
在阿里雲上測試了一個匯出資料大約為1.2G大小的集合,匯出效能如下:
- 時間:3分11秒
- 匯出1728423條記錄,每秒讀取記錄數=1728423/191=9050條/秒
- 匯出1264441038位元組,每秒處理位元組數=1264441038/191=6.6M/秒
-
將匯出的資料檔案 MY_COLLECTION_NAME.dmp 已任何方式(比如scp)同步到 Primary 節點所在的伺服器0上
-
匯入資料至副本集 Primay 節點
mongoimport -h my-repl/192.168.10.58:27017 -d MY_DB_NAME -c MY_COLLECTION_NAME --file MY_COLLECTION_NAME.dmp
測試匯入效能
- 時間:82秒
- 匯入1728423條記錄,每秒寫入記錄數=1728423/82=21078條/秒
- 匯入1264441038位元組,每秒處理位元組數=1264441038/82=14.7M/秒
注意:由於不是相同的伺服器,所以不能通過這個來比較匯入和匯出的效能差別,只能所謂一個參考:
-
總結:
- 在 Primary 節點匯入資料後,登入 Secondary 節點檢視,可以看到一百七十多萬條資料全部複製過去了,主從複製成功。
- 從效能分析結果來看,MongoDB的讀取和寫入效能是比較好的,特別是匯入的同時還要和副本之間同步資料。