參考鏈接:http://www.lanceyan.com/tech/mongodb/mongodb_repset1.html
一、MongoDB副本集介紹
前面我們介紹了安裝MongoDB及其相關的使用,但是其會存在下面的問題:
主節點掛了能否自動切換連接?目前需要手工切換。
主節點的讀寫壓力過大如何解決?
從節點每個上面的數據都是對數據庫全量拷貝,從節點壓力會不會過大?
數據壓力大到機器支撐不了的時候能否做到自動擴展?
這篇文章看完這些問題就可以搞定了。NoSQL的產生就是爲了解決大數據量、高擴展性、高性能、靈活數據模型、高可用性。但是光通過主從模式的架構遠遠達不到上面幾點,由此MongoDB設計了副本集和分片的功能。這篇文章主要介紹副本集。
注意:mongoDB官方已經不建議使用主從模式了,替代方案是採用副本集的模式。
1、簡介
那什麼是副本集呢?打魔獸世界總說打副本,其實這兩個概念差不多一個意思。遊戲裏的副本是指玩家集中在高峯時間去一個場景打怪,會出現玩家暴多怪物少的情況,遊戲開發商爲了保證玩家的體驗度,就爲每一批玩家單獨開放一個同樣的空間同樣的數量的怪物,這一個複製的場景就是一個副本,不管有多少個玩家各自在各自的副本里玩不會互相影響。 mongoDB的副本也是這個,主從模式其實就是一個單副本的應用,沒有很好的擴展性和容錯性。而副本集具有多個副本保證了容錯性,就算一個副本掛掉了還有很多副本存在,並且解決了上面第一個問題“主節點掛掉了,整個集羣內會自動切換”。難怪mongoDB官方推薦使用這種模式。我們來看看mongoDB副本集的架構圖:
由圖可以看到客戶端連接到整個副本集,不關心具體哪一臺機器是否掛掉。主服務器負責整個副本集的讀寫,副本集定期同步數據備份,一但主節點掛掉,副本節點就會選舉一個新的主服務器,這一切對於應用服務器不需要關心。我們看一下主服務器掛掉後的架構:
副本集中的副本節點在主節點掛掉後通過心跳機制檢測到後,就會在集羣內發起主節點的選舉機制,自動選舉一位新的主服務器。看起來很牛X的樣子,我們趕緊操作部署一下! 官方推薦的副本集機器數量爲至少3個,那我們也按照這個數量配置測試。
二、環境部署
1、環境介紹
2、同步時間
[[email protected] ~]# ntpdate 202.120.2.101 [[email protected] ~]# ntpdate 202.120.2.101 [[email protected] ~]# ntpdate 202.120.2.101
3、安裝mongodb
[[email protected] ~]# yum localinstall -y mongo-10gen-2.4.14-mongodb_1.x86_64.rpm mongo-10gen-server-2.4.14-mongodb_1.x86_64.rpm [[email protected] ~]# yum localinstall -y mongo-10gen-2.4.14-mongodb_1.x86_64.rpm mongo-10gen-server-2.4.14-mongodb_1.x86_64.rpm [[email protected] ~]# yum localinstall -y mongo-10gen-2.4.14-mongodb_1.x86_64.rpm mongo-10gen-server-2.4.14-mongodb_1.x86_64.rpm
4、數據庫初始化
[[email protected] ~]# mkdir /data/mongodb/ [[email protected] ~]# chown -R mongod.mongod /data/mongodb/ [[email protected] ~]# vim /etc/mongod.conf dbpath=/data/mongodb replSet = testrs0 rest = true [[email protected] ~]# service mongod restart Stopping mongod: [確定] Starting mongod: about to fork child process, waiting until server is ready for connections. forked process: 5376 all output going to: /var/log/mongo/mongod.log child process started successfully, parent exiting [確定] [[email protected] ~]# vim /etc/mongod.conf dbpath=/data/mongodb replSet = testrs0 rest = true [[email protected] ~]# mkdir -p /data/mongodb [[email protected] ~]# chown -R mongod.mongod /data/mongodb/ [[email protected] ~]# service mongod start Starting mongod: about to fork child process, waiting until server is ready for connections. forked process: 4131 all output going to: /var/log/mongo/mongod.log child process started successfully, parent exiting [確定] [[email protected] ~]# vim /etc/mongod.conf dbpath=/data/mongodb replSet = testrs0 rest = true [[email protected] ~]# mkdir -p /data/mongodb [[email protected] ~]# chown -R mongod.mongod /data/mongodb [[email protected] ~]# service mongod start Starting mongod: about to fork child process, waiting until server is ready for connections. forked process: 3143 all output going to: /var/log/mongo/mongod.log child process started successfully, parent exiting [確定]
主節點初始化:
> rs.initiate() { "info2" : "no configuration explicitly specified -- making one", "me" : "www.example.com:27017", "info" : "Config now saved locally. Should come online in about a minute.", "ok" : 1 } 查看當前節點的信息: testrs0:PRIMARY> rs.status() { "set" : "testrs0", "date" : ISODate("2015-09-26T10:49:29Z"), "myState" : 1, "members" : [ { "_id" : 0, "name" : "www.example.com:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 511, "optime" : Timestamp(1443264538, 1), "optimeDate" : ISODate("2015-09-26T10:48:58Z"), "self" : true } ], "ok" : 1 }
查看節點的信息:
testrs0:PRIMARY> db.isMaster() { "setName" : "testrs0", "ismaster" : true, "secondary" : false, "hosts" : [ " www.example.com:27017 " ], "primary" : " www.example.com:27017 ", "me" : " www.example.com:27017 ", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2015-09-26T10:51:02.547Z"), "ok" : 1 }
添加node1、node2節點並查看副本集的狀態:
testrs0:PRIMARY> rs.add("192.168.1.9:27017") { "ok" : 1 } testrs0:PRIMARY> rs.add("192.168.1.10:27017") { "ok" : 1 }testrs0:PRIMARY> rs.status() { "set" : "testrs0", "date" : ISODate("2015-09-26T10:55:13Z"), "myState" : 1, "members" : [ { "_id" : 0, "name" : "www.example.com:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 855, "optime" : Timestamp(1443264886, 1), "optimeDate" : ISODate("2015-09-26T10:54:46Z"), "self" : true }, { "_id" : 1, "name" : "192.168.1.9:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 131, "optime" : Timestamp(1443264886, 1), "optimeDate" : ISODate("2015-09-26T10:54:46Z"), "lastHeartbeat" : ISODate("2015-09-26T10:55:12Z"), "lastHeartbeatRecv" : ISODate("2015-09-26T10:55:13Z"), "pingMs" : 0, "syncingTo" : "www.example.com:27017" }, { "_id" : 2, "name" : "192.168.1.10:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 27, "optime" : Timestamp(1443264886, 1), "optimeDate" : ISODate("2015-09-26T10:54:46Z"), "lastHeartbeat" : ISODate("2015-09-26T10:55:12Z"), "lastHeartbeatRecv" : ISODate("2015-09-26T10:55:12Z"), "pingMs" : 0, "syncingTo" : "www.example.com:27017" } ], "ok" : 1 }
查看節點的信息:
testrs0:PRIMARY> db.isMaster() { "setName" : "testrs0", "ismaster" : true, "secondary" : false, "hosts" : [ " www.example.com:27017 ", "192.168.1.10:27017", "192.168.1.9:27017" ], "primary" : " www.example.com:27017 ", "me" : " www.example.com:27017 ", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2015-09-26T10:56:03.254Z"), "ok" : 1 }
登錄從節點查看各節點信息:
[[email protected] ~]# mongo MongoDB shell version: 2.4.14 connecting to: test testrs0:SECONDARY> db.isMaster() { "setName" : "testrs0", "ismaster" : false, "secondary" : true, "hosts" : [ "192.168.1.9:27017", "192.168.1.10:27017", "www.example.com:27017" ], "primary" : "www.example.com:27017", "me" : "192.168.1.9:27017", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2015-09-26T10:57:14.971Z"), "ok" : 1 }
6、登錄web窗口查看信息
登錄web接口,可以聽過Commands接口查看mongodb的信息:
點擊replSetGetStatus所查看的信息:
點擊isMaster所查看的信息:
7、驗證副本集同步數據
主節點插入數據:
testrs0:PRIMARY> db.testcoll.insert({Name: "bols",Age: 25,Gender: "F"}) testrs0:PRIMARY> db.testcoll.find() { "_id" : ObjectId("56067c642896c9fb293b5892"), "Name" : "bols", "Age" : 25, "Gender" : "F" }
從節點需要使用可以讀取數據的命令,才能查詢數據:
testrs0:SECONDARY> rs.slaveOk() testrs0:SECONDARY> db.testcoll.find() { "_id" : ObjectId("56067c642896c9fb293b5892"), "Name" : "bols", "Age" : 25, "Gender" : "F" }
[[email protected] ~]# mongo MongoDB shell version: 2.4.14 connecting to: test testrs0:SECONDARY> db.testcoll.find() error: { "$err" : "not master and slaveOk=false", "code" : 13435 } testrs0:SECONDARY> rs.slaveOk() testrs0:SECONDARY> db.testcoll.find() { "_id" : ObjectId("56067c642896c9fb293b5892"), "Name" : "bols", "Age" : 25, "Gender" : "F" }
8、驗證副本集是否能做到故障轉移
[
[email protected] ~]# service mongod stop Stopping mongod: [確定] 在node1節點上查看到主節點已轉移到node2節點上: testrs0:SECONDARY> rs.status() { "set" : "testrs0", "date" : ISODate("2015-09-26T11:14:02Z"), "myState" : 2, "syncingTo" : "192.168.1.10:27017", "members" : [ { "_id" : 0, "name" : "www.example.com:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : Timestamp(1443265636, 1), "optimeDate" : ISODate("2015-09-26T11:07:16Z"), "lastHeartbeat" : ISODate("2015-09-26T11:14:00Z"), "lastHeartbeatRecv" : ISODate("2015-09-26T11:13:43Z"), "pingMs" : 0 }, { "_id" : 1, "name" : "192.168.1.9:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 1783, "optime" : Timestamp(1443265636, 1), "optimeDate" : ISODate("2015-09-26T11:07:16Z"), "errmsg" : "syncing to: 192.168.1.10:27017", "self" : true }, { "_id" : 2, "name" : "192.168.1.10:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 45, "optime" : Timestamp(1443265636, 1), "optimeDate" : ISODate("2015-09-26T11:07:16Z"), "lastHeartbeat" : ISODate("2015-09-26T11:14:01Z"), "lastHeartbeatRecv" : ISODate("2015-09-26T11:14:01Z"), "pingMs" : 1, "syncingTo" : "www.example.com:27017" } ], "ok" : 1 }
通過node2節點查看其是否爲主:
testrs0:PRIMARY> db.isMaster() { "setName" : "testrs0", "ismaster" : true, "secondary" : false, "hosts" : [ "192.168.1.10:27017", "192.168.1.9:27017", " www.example.com:27017 " ], "primary" : "192.168.1.10:27017", "me" : "192.168.1.10:27017", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2015-09-26T11:15:48.726Z"), "ok" : 1 }
node2上插入數據:
testrs0:PRIMARY> db.testcoll.insert({Name: "longls",Age: 24,Gender: "F"}) testrs0:PRIMARY> db.testcoll.find() { "_id" : ObjectId("56067c642896c9fb293b5892"), "Name" : "bols", "Age" : 25, "Gender" : "F" } { "_id" : ObjectId("56067ec6e121af6dd6618634"), "Name" : "longls", "Age" : 24, "Gender" : "F" }
原主節點恢復使用,發現不能奪回成爲主節點:
[[email protected] ~]# service mongod start Starting mongod: about to fork child process, waiting until server is ready for connections. forked process: 5955 all output going to: /var/log/mongo/mongod.log child process started successfully, parent exiting [確定] [[email protected] ~]# mongo MongoDB shell version: 2.4.14 connecting to: test testrs0:SECONDARY> rs.status() { "set" : "testrs0", "date" : ISODate("2015-09-26T11:18:31Z"), "myState" : 2, "syncingTo" : "192.168.1.9:27017", "members" : [ { "_id" : 0, "name" : "www.example.com:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 20, "optime" : Timestamp(1443266246, 1), "optimeDate" : ISODate("2015-09-26T11:17:26Z"), "errmsg" : "syncing to: 192.168.1.9:27017", "self" : true }, { "_id" : 1, "name" : "192.168.1.9:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 20, "optime" : Timestamp(1443266246, 1), "optimeDate" : ISODate("2015-09-26T11:17:26Z"), "lastHeartbeat" : ISODate("2015-09-26T11:18:31Z"), "lastHeartbeatRecv" : ISODate("2015-09-26T11:18:30Z"), "pingMs" : 0, "syncingTo" : "192.168.1.10:27017" }, { "_id" : 2, "name" : "192.168.1.10:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 20, "optime" : Timestamp(1443266246, 1), "optimeDate" : ISODate("2015-09-26T11:17:26Z"), "lastHeartbeat" : ISODate("2015-09-26T11:18:31Z"), "lastHeartbeatRecv" : ISODate("2015-09-26T11:18:30Z"), "pingMs" : 0 } ], "ok" : 1 }
查看是否能夠查詢剛纔在node2節點上插入的數據:
testrs0:SECONDARY> rs.slaveOk() testrs0:SECONDARY> db.testcoll.find() { "_id" : ObjectId("56067c642896c9fb293b5892"), "Name" : "bols", "Age" : 25, "Gender" : "F" } { "_id" : ObjectId("56067ec6e121af6dd6618634"), "Name" : "longls", "Age" : 24, "Gender" : "F" }
查看複製的信息:
testrs0:SECONDARY> db.printReplicationInfo() configured oplog size: 990MB log length start to end: 1708secs (0.47hrs) oplog first event time: Sat Sep 26 2015 18:48:58 GMT+0800 (CST) oplog last event time: Sat Sep 26 2015 19:17:26 GMT+0800 (CST) now: Sat Sep 26 2015 19:22:38 GMT+0800 (CST)
9、定義www.example.com節點一上線就爲主節點
自定義配置文件並更改www.example.com節點的的權重:
testrs0:PRIMARY> mycfg=rs.conf() { "_id" : "testrs0", "version" : 3, "members" : [ { "_id" : 0, "host" : " www.example.com:27017 " }, { "_id" : 1, "host" : "192.168.1.9:27017" }, { "_id" : 2, "host" : "192.168.1.10:27017" } ] } testrs0:PRIMARY> mycfg.members[0].priority=2 2 testrs0:PRIMARY> rs.reconfig(mycfg) Sat Sep 26 19:31:20.095 DBClientCursor::init call() failed Sat Sep 26 19:31:20.096 trying reconnect to 127.0.0.1:27017 Sat Sep 26 19:31:20.114 reconnect 127.0.0.1:27017 ok reconnected to server after rs command (which is normal)
查看是否生效:
testrs0:SECONDARY> rs.config() { "_id" : "testrs0", "version" : 4, "members" : [ { "_id" : 0, "host" : " www.example.com:27017 ", "priority" : 2 }, { "_id" : 1, "host" : "192.168.1.9:27017" }, { "_id" : 2, "host" : "192.168.1.10:27017" } ] }
驗證是否生效:
[[email protected] ~]# service mongod stop Stopping mongod: [確定] [[email protected] ~]# service mongod start Starting mongod: about to fork child process, waiting until server is ready for connections. forked process: 6214 all output going to: /var/log/mongo/mongod.log child process started successfully, parent exiting [確定] [[email protected] ~]# mongo MongoDB shell version: 2.4.14 connecting to: test testrs0:SECONDARY> db.isMaster() { "setName" : "testrs0", "ismaster" : false, "secondary" : true, "hosts" : [ "www.example.com:27017", "192.168.1.10:27017", "192.168.1.9:27017" ], "me" : "www.example.com:27017", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2015-09-26T11:35:32.082Z"), "ok" : 1 } testrs0:PRIMARY> db.isMaster() { "setName" : "testrs0", "ismaster" : true, "secondary" : false, "hosts" : [ "www.example.com:27017", "192.168.1.10:27017", "192.168.1.9:27017" ], "primary" : "www.example.com:27017", "me" : "www.example.com:27017", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2015-09-26T11:36:37.230Z"), "ok" : 1 }
通過上面可以發現一旦主節點故障下線再次上線之後並不能直接就是主節點,還需要等一段時間即需要出發選舉機制才能再次成爲主節點。