MongoDB復制集架構
由數據結點,投票結點組成,需要配置集群信息,可自動檢測集群中的結點健康狀態,當有結點出故障時,自動下線和切換主從結點。適用於數據量較大,高可用服務
通常,為了防止單點故障應用程序需要做集群。然而在數據庫中除了防止單點故障,還需要做到數據庫備份,讀寫分離,故障轉移等。而 MongoDB 的 Replica Set 恰恰都能滿足這些要求。
Replica Set角色
Replica Set 的成員是一堆有著同樣的數據內容 mongod 的實例集合,包含以下三類角色:
主節點(Primary)
是 Replica Set 中唯一的接收寫請求的節點,並將寫入指令記錄到 oplog 上。副本節點通過復制 oplog 的寫入指令同步主節點的數據。Secondary。一個 Replica Set 有且只有Primary 節點,當Primar掛掉後,其他 Secondary 或者 Arbiter 節點會重新選舉出來一個主節點。應用程序的默認讀取請求也是發到 Primary節點處理的。副本節點(Secondary)
通過復制主節點 oplog 中的指令與主節點保持同樣的數據集,當主節點掛掉的時候,參與主節點選舉。仲裁者(Arbiter)
不存儲實際應用數據,只投票參與選取主節點,但不會被選舉成為主節點。
復制集架構配置:
架構:
Arbite:127.0.0.1:27017
primary:127.0.0.1:27018
secondary: 127.0.0.1:27019
配置文件內容如下,只要修改logpath、dbpath、pidfilepath和端口即可
arbiter.conf
# syslog配置 logpath=/var/log/mongo/logs/mongod1.log logappend=true timeStampFormat=iso8601-utc # storage 存儲配置 dbpath=/data/mongo/repldb1 directoryperdb=true # processManagement 進程管理配置 fork=true # 進程 PID 文件保存目錄 pidfilepath=/var/run/mongod1.pid # net 網絡配置 bind_ip=127.0.0.1 port=27017 # security 配置 #auth=true # 復制集 replSet=test-set
primary.conf
# syslog配置 logpath=/var/log/mongo/logs/mongod2.log logappend=true timeStampFormat=iso8601-utc # storage 存儲配置 dbpath=/data/mongo/repldb2 directoryperdb=true # processManagement 進程管理配置 fork=true # 進程 PID 文件保存目錄 pidfilepath=/var/run/mongod2.pid # net 網絡配置 bind_ip=127.0.0.1 port=27018 # security 配置 #auth=true # 復制集 replSet=test-set
secondary.conf
# syslog配置 logpath=/var/log/mongo/logs/mongod3.log logappend=true timeStampFormat=iso8601-utc # storage 存儲配置 dbpath=/data/mongo/repldb3 directoryperdb=true # processManagement 進程管理配置 fork=true # 進程 PID 文件保存目錄 pidfilepath=/var/run/mongod3.pid # net 網絡配置 bind_ip=127.0.0.1 port=27019 # security 配置 #auth=true # 復制集 replSet=test-set
分別啟動這三個實例:
mongod --config arbiter.conf mongod --config primary.conf mongod --config secondary.conf
初始化:
使用mongodb客戶端登陸兩個常規節點(primary和secondary)中的任何一個,執行如下命令
mongod 127.0.0.1:27018
登陸後執行初始化命令
> rs.initiate( ... {"_id":"test-set", ... "members":[ ... {"_id":1,"host":"127.0.0.1:27018"}, ... {"_id":2,"host":"127.0.0.1:27019"} ... ] ... }); { "ok" : 1 } test-set:SECONDARY> rs.addArb("127.0.0.1:27017") { "ok" : 1 }
查看集群狀態:
test-set:PRIMARY> rs.status() { "set" : "test-set", "date" : ISODate("2018-04-19T07:22:22.813Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1524122535, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1524122535, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1524122535, 1), "t" : NumberLong(1) } }, "members" : [ { "_id" : 1, "name" : "127.0.0.1:27018", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1303, "optime" : { "ts" : Timestamp(1524122535, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2018-04-19T07:22:15Z"), "electionTime" : Timestamp(1524121903, 1), "electionDate" : ISODate("2018-04-19T07:11:43Z"), "configVersion" : 2, "self" : true }, { "_id" : 2, "name" : "127.0.0.1:27019", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 649, "optime" : { "ts" : Timestamp(1524122535, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1524122535, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2018-04-19T07:22:15Z"), "optimeDurableDate" : ISODate("2018-04-19T07:22:15Z"), "lastHeartbeat" : ISODate("2018-04-19T07:22:21.472Z"), "lastHeartbeatRecv" : ISODate("2018-04-19T07:22:21.472Z"), "pingMs" : NumberLong(0), "syncingTo" : "127.0.0.1:27018", "configVersion" : 2 }, { "_id" : 3, "name" : "127.0.0.1:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 69, "lastHeartbeat" : ISODate("2018-04-19T07:22:21.471Z"), "lastHeartbeatRecv" : ISODate("2018-04-19T07:22:18.484Z"), "pingMs" : NumberLong(0), "configVersion" : 2 } ], "ok" : 1 }
登錄127.0.0.1:27019 角色顯示為Secondary
mongo 127.0.0.1:27019
2018-04-19T07:00:44.075Z I CONTROL [initandlisten] test-set:SECONDARY> test-set:SECONDARY> test-set:SECONDARY>
在primary上創建數據,測試secondary上是否會同步
test-set:PRIMARY> show dbs admin 0.000GB local 0.000GB test-set:PRIMARY> use testdb switched to db testdb test-set:PRIMARY> show collections test-set:PRIMARY> db.test.insert({"name":"user1"}); WriteResult({ "nInserted" : 1 })
登錄secondary,查看
test-set:SECONDARY> rs.slaveOk() test-set:SECONDARY> show dbs admin 0.000GB local 0.000GB testdb 0.000GB test-set:SECONDARY> use testdb switched to db testdb test-set:SECONDARY> show collections test test-set:SECONDARY> db.test.find() { "_id" : ObjectId("5ad844efa46a748c5a90fe41"), "name" : "user1" }
secondary上插入數據報錯,說明secondary上沒有寫的權限
test-set:SECONDARY> db.test.insert({"name":"user2"}); WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } })
驗證復制集架構容災功能
kill掉primary的進程,登錄127.0.0.1:27019
test-set:PRIMARY> show dbs admin 0.000GB local 0.000GB testdb 0.000GB test-set:PRIMARY> rs.status() { "set" : "test-set", "date" : ISODate("2018-04-19T07:44:28.912Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1524123775, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1524123862, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1524123862, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 1, "name" : "127.0.0.1:27018", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDurable" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2018-04-19T07:44:27.023Z"), "lastHeartbeatRecv" : ISODate("2018-04-19T07:42:59.830Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 2, "name" : "127.0.0.1:27019", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 2625, "optime" : { "ts" : Timestamp(1524123862, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-04-19T07:44:22Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1524123790, 1), "electionDate" : ISODate("2018-04-19T07:43:10Z"), "configVersion" : 2, "self" : true }, { "_id" : 3, "name" : "127.0.0.1:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 1395, "lastHeartbeat" : ISODate("2018-04-19T07:44:27.009Z"), "lastHeartbeatRecv" : ISODate("2018-04-19T07:44:28.662Z"), "pingMs" : NumberLong(0), "configVersion" : 2 } ], "ok" : 1 }
可以看到127.0.0.1:27018 有一個 "stateStr" : "(not reachable/healthy)"的報錯,而127.0.0.1:27019已經變成了primary角色
啟動原來的primary.conf配置的實例,角色由原來的primary變成了secondary
test-set:SECONDARY> show dbs 2018-04-19T15:50:04.113+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotMasterNoSlaveOk" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1 shellHelper.show@src/mongo/shell/utils.js:782:19 shellHelper@src/mongo/shell/utils.js:672:15 @(shellhelp2):1:1 test-set:SECONDARY>
其他命令:
rs.slaveOk():secondary默認不允許讀寫,執行此命令後,從節點有可讀權限
rs.conf():查看配置情況
test-set:SECONDARY> rs.conf() { "_id" : "test-set", "version" : 2, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 1, "host" : "127.0.0.1:27018", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "127.0.0.1:27019", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 3, "host" : "127.0.0.1:27017", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : 60000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5ad841247ffb05dbb6b4a13b") } }
MongoDB復制集架構