MongoDB復制集是一個帶有故障轉移的主從集群。是從現有的主從模式演變而來,增加了自動故障轉移和節點成員自動恢復。MongoDB復制集模式中沒有固定的主結點,在啟動后,多個服務節點間將自動選舉產生一個主結點。該主結點被稱為primary,一個或多個從結點被稱為secondaries。primary結點基本上就是master結點,不同之處在於primary結點在不同時間可能是不同的服務器。如果當前的主結點失效了,復制集中的其余結點將會試圖選出一個新的主結點。
MongoDB復制集模式的好處:
- 一切自動化。首先,復制集模式本身做了大量的管理工作,自動管理從節點,確保數據不會不一致。
- 主節點掛掉后,會自動判斷集群中的服務器並進行故障轉移,推舉新的主節點。
- 一個復制集集群支持1-7台服務器,在一個復制集中各個服務器數據保持完全一致。
在一個MongoDB復制集集群中,各個服務器有以下幾種狀態:
- Primary 主節點,一個復制集有且僅有一台服務器處於Primary狀態,只有主節點才對外提供讀寫服務。如果主節點掛掉,復制集將投票選出一個備節點成為新的主節點。
- Secondary 備用節點,復制集允許有多台Secondary,每個備用節點的數據與主節點的數據是完全同步的。Recovering 恢復中,當復制集中某台服務器掛掉或者掉線后數據無法同步,重新恢復服務后從其他成員復制數據,這時就處於恢復過程,數據同步后,該節點又回到備用狀態。
- Arbiter 仲裁節點,該類節點可以不用單獨存在,如果配置為仲裁節點,就主要負責在復本集中監控其他節點狀態,投票選出主節點。該節點將不會用於存放數據。如果沒有仲裁節點,那么投票工作將由所有節點共同進行。
- Down 無效節點,當服務器掛掉或掉線時就會處於該狀態。復制集的從節點讀請求,也是在各個Driver層設置slaveOk的值來實現的。
如上介紹所知,mongodb中的復制可以在多台服務器中同步數據。復制提供了冗余和增加了數據的高可用性,防止單個節點易丟失數據的可能性,也可以用來進行讀寫分離提高客戶端操作性能。復制集中各節點的mongodb實例有相同的數據集副本。主節點可以接收客戶端所有寫操作記錄到日志中,從庫復制主庫的操作日志記錄應用到其數據庫中。一個客戶端只能有一個主節點,如果主節點不可用(10秒內無法連接),復制集中將選一個成員節點作為主節點。
MongoDB主備+仲裁的基本結構如下:
主節點(Primary)
在復制集中,主節點是唯一能夠接收寫請求的節點。MongoDB在主節點進行寫操作,並將這些操作記錄到主節點的oplog中。而從節點將會從oplog復制到其本機,並將這些操作應用到自己的數據集上。(復制集最多只能擁有一個主節點)
從節點(Secondaries)
從節點通過應用主節點傳來的數據變動操作來保持其數據集與主節點一致。從節點也可以通過增加額外參數配置來對應特殊需求。例如,從節點可以是non-voting或是priority 0.
仲裁節點(ARBITER)
仲裁節點即投票節點,其本身並不包含數據集,且也無法晉升為主節點。但是,旦當前的主節點不可用時,投票節點就會參與到新的主節點選舉的投票中。仲裁節點使用最小的資源並且不要求硬件設備。投票節點的存在使得復制集可以以偶數個節點存在,而無需為復制集再新增節點 不要將投票節點運行在復制集的主節點或從節點機器上。 投票節點與其他 復制集節點的交流僅有:選舉過程中的投票,心跳檢測和配置數據。這些交互都是不加密的。
心跳檢測
復制集成員每兩秒向復制集中其他成員進行心跳檢測。如果某個節點在10秒內沒有返回,那么它將被標記為不可用。
MongoDB副本集是有故障恢復功能的主從集群,由一個primary節點和一個或多個secondary節點組成:
節點同步過程: Primary節點寫入數據,Secondary通過讀取Primary的oplog得到復制信息,開始復制數據並且將復制信息寫入到自己的oplog。如果某個操作失敗,則備份節點停止從當前數據源復制數據。如果某個備份節點由於某些原因掛掉了,當重新啟動后,就會自動從oplog的最后一個操作開始同步,同步完成后,將信息寫入自己的oplog,由於復制操作是先復制數據,復制完成后再寫入oplog,有可能相同的操作會同步兩份,不過MongoDB在設計之初就考慮到這個問題,將oplog的同一個操作執行多次,與執行一次的效果是一樣的。
通俗理解:當Primary節點完成數據操作后,Secondary會做出一系列的動作保證數據的同步:
- 檢查自己local庫的oplog.rs集合,找出最近的時間戳。
- 檢查Primary節點local庫oplog.rs集合,找出大於此時間戳的記錄。
- 將找到的記錄插入到自己的oplog.rs集合中,並執行這些操作。
副本集的同步和主從同步一樣,都是異步同步的過程,不同的是副本集有個自動故障轉移的功能。其原理是:slave端從primary端獲取日志,然后在自己身上完全順序的執行日志所記錄的各種操作(該日志是不記錄查詢操作的),這個日志就是local數據 庫中的oplog.rs表,默認在64位機器上這個表是比較大的,占磁盤大小的5%,oplog.rs的大小可以在啟動參數中設 定:–oplogSize 1000,單位是M。
注意:在副本集的環境中,要是所有的Secondary都宕機了,只剩下Primary。最后Primary會變成Secondary,不能提供服務。
下面簡單介紹下MongoDB 副本集的部署過程:
1)服務器信息 sign-mongo01.wangshibo.cn 172.16.51.216 Primary sign-mongo02.wangshibo.cn 172.16.51.217 Secondary sign-mongo03.wangshibo.cn 172.16.51.218 Arbiter 三台服務器均設置好主機名,關閉iptables及selinux(略) 2)在3台服務器文件hosts文件中都添加以下3行: [root@sign-mongo01 ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 172.16.51.216 sign-mongo01.wangshibo.cn 172.16.51.217 sign-mongo02.wangshibo.cn 172.16.51.218 sign-mongo03.wangshibo.cn 3)安裝部署mongodb(三台機器都安裝) 下載地址:https://www.mongodb.org/dl/linux/x86_64-rhel62 [app@sign-mongo01 software]$ pwd /data/software [app@sign-mongo01 software]$ ls mongodb-linux-x86_64-rhel62-v3.2-latest.tgz [app@sign-mongo01 software]$ tar -zvxf mongodb-linux-x86_64-rhel62-v3.2-latest.tgz [app@sign-mongo01 software]$ mv mongodb-linux-x86_64-rhel62-3.2.17-34-g4c1bae566c /data/mongodb [app@sign-mongo01 software]$ cd /data/mongodb/ [app@sign-mongo01 mongodb]$ mkdir data [app@sign-mongo01 mongodb]$ mkdir log [app@sign-mongo01 mongodb]$ vim mongodb.conf pidfilepath=/data/mongodb/log/mongod.pid logpath=/data/mongodb/log/mongod.log dbpath=/data/mongodb logappend=true bind_ip=172.16.51.216 port=27017 fork=true replSet=rs0 備節點的mongodb.conf配置文件分別為: [app@sign-mongo02 mongodb]$ vim mongodb.conf pidfilepath=/data/mongodb/log/mongod.pid logpath=/data/mongodb/log/mongod.log dbpath=/data/mongodb logappend=true bind_ip=172.16.51.217 port=27018 fork=true replSet=rs0 其中:replSet=rs0 #表示復制集名稱:rs0 啟動主備服務器的mongodb [app@sign-mongo01 ~]$ /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf about to fork child process, waiting until server is ready for connections. forked process: 7317 child process started successfully, parent exiting [app@sign-mongo01 mongodb]$ ps -ef|grep mongodb app 7317 1 0 21:38 ? 00:00:00 /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf app 7342 7254 0 21:38 pts/1 00:00:00 grep mongodb [app@sign-mongo01 mongodb]$ lsof -i:27017 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME mongod 7317 app 6u IPv4 27011 0t0 TCP sign-mongo01.wangshibo.cn:27017 (LISTEN) [app@sign-mongo02 mongodb]$ /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf about to fork child process, waiting until server is ready for connections. forked process: 9725 child process started successfully, parent exiting [app@sign-mongo02 mongodb]$ lsof -i:27018 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME mongod 9725 app 6u IPv4 27191 0t0 TCP sign-mongo02.wangshibo.cn:27018 (LISTEN) 設置mongodb的環變量 [root@sign-mongo01 src]# vim /etc/profile ...... export PATH=$PATH:/data/mongodb/bin [root@sign-mongo01 src]# source /etc/profile 登錄到mongodb中: [app@sign-mongo01 ~]$ mongo 172.16.51.216:27017 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.216:27017/test Welcome to the MongoDB shell. For interactive help, type "help". For more comprehensive documentation, see http://docs.mongodb.org/ Questions? Try the support group http://groups.google.com/group/mongodb-user Server has startup warnings: 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:38:18.063+0800 I CONTROL [initandlisten] > 初始化復制集:(集合為:"rs0" ;第一個成員為:"sign-mongo01.wangshibo.cn:27017" > rs.initiate({_id: "rs0",members: [{ _id: 0 , host: "sign-mongo01.wangshibo.cn:27017" }]}) { "ok" : 1 } rs0:OTHER> //接着回車,顯示這個節點為Primary主節點 rs0:PRIMARY> 接着添加另1個成員: rs0:PRIMARY> rs.add("sign-mongo02.wangshibo.cn:27018") { "ok" : 1 } 查看成員信息 (或者使用 db.isMaster() ) rs0:PRIMARY> rs.status() { "set" : "rs0", "date" : ISODate("2017-11-22T13:55:28.446Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "sign-mongo01.wangshibo.cn:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1031, "optime" : { "ts" : Timestamp(1511358895, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-22T13:54:55Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1511358843, 2), "electionDate" : ISODate("2017-11-22T13:54:03Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "sign-mongo02.wangshibo.cn:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 32, "optime" : { "ts" : Timestamp(1511358895, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-22T13:54:55Z"), "lastHeartbeat" : ISODate("2017-11-22T13:55:27.684Z"), "lastHeartbeatRecv" : ISODate("2017-11-22T13:55:27.827Z"), "pingMs" : NumberLong(0), "configVersion" : 2 } ], "ok" : 1 } 或者使用該方法查看,結果也是一樣: rs0:PRIMARY> use admin switched to db admin rs0:PRIMARY> db.runCommand( { replSetGetStatus : 1 } ) { "set" : "rs0", "date" : ISODate("2017-11-23T01:12:20.701Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "sign-mongo01.wangshibo.cn:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 41643, "optime" : { "ts" : Timestamp(1511358895, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-22T13:54:55Z"), "electionTime" : Timestamp(1511358843, 2), "electionDate" : ISODate("2017-11-22T13:54:03Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "sign-mongo02.wangshibo.cn:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 40645, "optime" : { "ts" : Timestamp(1511358895, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-22T13:54:55Z"), "lastHeartbeat" : ISODate("2017-11-23T01:12:19.418Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T01:12:16.225Z"), "pingMs" : NumberLong(0), "configVersion" : 2 } ], "ok" : 1 } rs0:PRIMARY> 詳細說明如下: "_id" : #集群中節點編號 "name" : #成員服務器名稱及端口 "health" : #表示成員中的健康狀態(0:down;1:up) "state" : #為0~10,表示成員的當前狀態 "stateStr" : #描述該成員是主庫(PRIMARY)還是備庫(SECONDARY) "uptime" : #該成員在線時間(秒) "optime" : #成員最后一次應用日志(oplog)的信息 "optimeDate" : #成員最后一次應用日志(oplog)的時間 "electionTime" : #當前primary從操作日志中選舉信息 "electionDate" : #當前primary被選定為primary的日期 "configVersion" : #mongodb版本 "self" : #為true 表示當前節點 4)測試操作。在主庫中,可以任意操作: rs0:PRIMARY> show dbs local 0.000GB rs0:PRIMARY> use mydb //切換到要創建的數據庫 switched to db mydb rs0:PRIMARY> show dbs //use只是轉到相關數據庫,此時並沒有做任何操作,所以並不會創建相應的數據庫,只有當真正的操作了一次數據庫就會自動創建。 local 0.000GB rs0:PRIMARY> db.stats(); { "db" : "mydb", "collections" : 0, "objects" : 0, "avgObjSize" : 0, "dataSize" : 0, "storageSize" : 0, "numExtents" : 0, "indexes" : 0, "indexSize" : 0, "fileSize" : 0, "ok" : 1 } rs0:PRIMARY> db.coll.insert({"id":1}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> db.coll.find() { "_id" : ObjectId("5a162222991b83743942d169"), "id" : 1 } rs0:PRIMARY> db.coll.remove({"id":1}) WriteResult({ "nRemoved" : 1 }) rs0:PRIMARY> show dbs local 0.000GB mydb 0.000GB 現在到備庫中 172.16.51.217 (sign-mongo02.wangshibo.cn ) 查看分庫數據庫目錄,發現多了數據庫,數據庫與主庫(172.16.51.216)一致!是主庫同步過來的。 [app@sign-mongo02 ~]$ mongo 172.16.51.217:27018 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.217:27018/test Server has startup warnings: 2017-11-22T21:46:38.417+0800 I CONTROL [initandlisten] 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] 在副本服務器中登錄其本地數據庫,發現可以連接,但是無法讀寫操作: rs0:SECONDARY> show dbs 2017-11-23T09:25:35.961+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } : _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:781:19 shellHelper@src/mongo/shell/utils.js:671:15 @(shellhelp2):1:1 rs0:SECONDARY> 從庫開啟讀操作(此時可以測試主庫插入,從庫查看,同步正常): rs0:SECONDARY> rs.slaveOk(); rs0:SECONDARY> show dbs local 0.000GB mydb 0.000GB 5)現在模擬主庫不可用,將主節點服務停止: [app@sign-mongo01 mongodb]$ pkill -9 mongod [app@sign-mongo01 mongodb]$ ps -ef|grep mongodb app 9524 9398 0 09:32 pts/0 00:00:00 grep mongodb 到備節點172.16.51.217 中登錄mongodb,查看復制集狀態: [app@sign-mongo02 ~]$ mongo 172.16.51.217:27018 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.217:27018/test Server has startup warnings: 2017-11-22T21:46:38.417+0800 I CONTROL [initandlisten] 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] rs0:SECONDARY> rs.status() { "set" : "rs0", "date" : ISODate("2017-11-23T01:33:29.688Z"), "myState" : 2, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "sign-mongo01.wangshibo.cn:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2017-11-23T01:33:28.732Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T01:32:28.099Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "sign-mongo02.wangshibo.cn:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 42412, "optime" : { "ts" : Timestamp(1511399985, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-23T01:19:45Z"), "infoMessage" : "could not find member to sync from", "configVersion" : 2, "self" : true } ], "ok" : 1 } 有上面可看出,主節點刪除服務進程,primary並沒有切換到備節點上: 再次啟動主節點的mongodb服務,發現primary才自動切換回到主節點: [app@sign-mongo01 mongodb]$ nohup /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf nohup: ignoring input and appending output to `nohup.out' [app@sign-mongo01 mongodb]$ ps -ef|grep mongodb app 9538 1 8 09:37 ? 00:00:00 /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf app 9610 9398 0 09:37 pts/0 00:00:00 grep mongodb [app@sign-mongo01 mongodb]$ mongo 172.16.51.216:27017 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.216:27017/test Server has startup warnings: 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] rs0:PRIMARY> rs.status() { "set" : "rs0", "date" : ISODate("2017-11-23T01:38:19.631Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "sign-mongo01.wangshibo.cn:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 54, "optime" : { "ts" : Timestamp(1511401058, 2), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-23T01:37:38Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1511401058, 1), "electionDate" : ISODate("2017-11-23T01:37:38Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "sign-mongo02.wangshibo.cn:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 53, "optime" : { "ts" : Timestamp(1511401058, 2), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-23T01:37:38Z"), "lastHeartbeat" : ISODate("2017-11-23T01:38:18.072Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T01:38:18.596Z"), "pingMs" : NumberLong(0), "syncingTo" : "sign-mongo01.wangshibo.cn:27017", "configVersion" : 2 } ], "ok" : 1 } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 現在開始部署仲裁節點sign-mongo03.wangshibo.cn [app@sign-mongo03 ~]$ mkdir /data/mongodb/arbiter [app@sign-mongo03 ~]$ ll -d /data/mongodb/arbiter drwxrwxr-x 2 app app 4096 Nov 23 09:39 /data/mongodb/arbiter 本案例是在普通用戶app賬號下部署的,權限都是app.app。 如果是在root賬號下部署,那么需要將mongodb數據目錄下的文件全部設置mongodb.mongodb權限 mongodb.conf配置: [app@sign-mongo03 ~]$ vim /data/mongodb/mongodb.conf pidfilepath=/data/mongodb/log/mongod.pid logpath=/data/mongodb/log/mongod.log dbpath=/data/mongodb/arbiter logappend=false bind_ip=172.16.51.218 port=27019 fork=true replSet=rs0 接着重啟服務: [app@sign-mongo03 ~]$ /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf about to fork child process, waiting until server is ready for connections. forked process: 9217 child process started successfully, parent exiting [app@sign-mongo03 ~]$ ps -ef|grep mongo app 9217 1 1 09:46 ? 00:00:00 /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf app 9242 9158 0 09:46 pts/0 00:00:00 grep mongo [app@sign-mongo03 ~]$ lsof -i:27019 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME mongod 9217 app 6u IPv4 37321 0t0 TCP sign-mongo03.wangshibo.cn:27019 (LISTEN) 然后在Paimary主節點sign-mongo01.wangshibo.cn的mongodb中添加仲裁節點並查看結果 [app@sign-mongo01 mongodb]$ mongo 172.16.51.216:27017 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.216:27017/test Server has startup warnings: 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-23T09:37:26.467+0800 I CONTROL [initandlisten] rs0:PRIMARY> rs.addArb("sign-mongo03.wangshibo.cn:27019") { "ok" : 1 } rs0:PRIMARY> db.isMaster() { "hosts" : [ "sign-mongo01.wangshibo.cn:27017", "sign-mongo02.wangshibo.cn:27018" ], "arbiters" : [ "sign-mongo03.wangshibo.cn:27019" ], "setName" : "rs0", "setVersion" : 4, "ismaster" : true, "secondary" : false, "primary" : "sign-mongo01.wangshibo.cn:27017", "me" : "sign-mongo01.wangshibo.cn:27017", "electionId" : ObjectId("7fffffff0000000000000003"), "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "maxWriteBatchSize" : 1000, "localTime" : ISODate("2017-11-23T01:59:03.554Z"), "maxWireVersion" : 4, "minWireVersion" : 0, "ok" : 1 } rs0:PRIMARY> rs.status() { "set" : "rs0", "date" : ISODate("2017-11-23T02:00:26.312Z"), "myState" : 1, "term" : NumberLong(3), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "sign-mongo01.wangshibo.cn:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 368, "optime" : { "ts" : Timestamp(1511402420, 1), "t" : NumberLong(3) }, "optimeDate" : ISODate("2017-11-23T02:00:20Z"), "electionTime" : Timestamp(1511402069, 1), "electionDate" : ISODate("2017-11-23T01:54:29Z"), "configVersion" : 5, "self" : true }, { "_id" : 1, "name" : "sign-mongo02.wangshibo.cn:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 367, "optime" : { "ts" : Timestamp(1511402420, 1), "t" : NumberLong(3) }, "optimeDate" : ISODate("2017-11-23T02:00:20Z"), "lastHeartbeat" : ISODate("2017-11-23T02:00:26.306Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T02:00:26.309Z"), "pingMs" : NumberLong(0), "syncingTo" : "sign-mongo01.wangshibo.cn:27017", "configVersion" : 5 }, { "_id" : 3, "name" : "sign-mongo03.wangshibo.cn:27019", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 93, "lastHeartbeat" : ISODate("2017-11-23T02:00:26.306Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T02:00:25.306Z"), "pingMs" : NumberLong(0), "configVersion" : 5 } ], "ok" : 1 } 至此,添加完成!! 再次測試主備切換。關閉172.16.51.216(primary)中刪除服務進程: [app@sign-mongo01 mongodb]$ pkill -9 mongod [app@sign-mongo01 mongodb]$ ps -ef|grep mongod app 9664 9398 0 09:50 pts/0 00:00:00 grep mongod [app@sign-mongo01 mongodb]$ lsof -i:27017 [app@sign-mongo01 mongodb]$ 然后到172.16.51.217:27018(secondary)查看,發現primary已經切換為172.16.51.217 [app@sign-mongo02 ~]$ mongo 172.16.51.217:27018 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.217:27018/test Server has startup warnings: 2017-11-22T21:46:38.417+0800 I CONTROL [initandlisten] 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-22T21:46:38.418+0800 I CONTROL [initandlisten] rs0:PRIMARY> rs.status() { "set" : "rs0", "date" : ISODate("2017-11-23T02:01:17.762Z"), "myState" : 1, "term" : NumberLong(4), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "sign-mongo01.wangshibo.cn:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2017-11-23T02:01:17.316Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T02:01:04.327Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "sign-mongo02.wangshibo.cn:27018", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 44080, "optime" : { "ts" : Timestamp(1511402475, 2), "t" : NumberLong(4) }, "optimeDate" : ISODate("2017-11-23T02:01:15Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1511402475, 1), "electionDate" : ISODate("2017-11-23T02:01:15Z"), "configVersion" : 5, "self" : true }, { "_id" : 3, "name" : "sign-mongo03.wangshibo.cn:27019", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 145, "lastHeartbeat" : ISODate("2017-11-23T02:01:17.315Z"), "lastHeartbeatRecv" : ISODate("2017-11-23T02:01:15.316Z"), "pingMs" : NumberLong(0), "configVersion" : 5 } ], "ok" : 1 } rs0:PRIMARY> show dbs local 0.000GB mydb 0.000GB 有上面信息可知,添加仲裁節點后,primary能正常啟動切換了!~ 現在看看arbiter,連接到172.16.51.218:27019 [app@sign-mongo03 ~]$ mongo 172.16.51.218:27019 MongoDB shell version: 3.2.17-34-g4c1bae566c connecting to: 172.16.51.218:27019/test Welcome to the MongoDB shell. For interactive help, type "help". For more comprehensive documentation, see http://docs.mongodb.org/ Questions? Try the support group http://groups.google.com/group/mongodb-user Server has startup warnings: 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2017-11-23T09:58:34.978+0800 I CONTROL [initandlisten] rs0:ARBITER> rs.slaveOk(); rs0:ARBITER> db.isMaster() { "hosts" : [ "sign-mongo01.wangshibo.cn:27017", "sign-mongo02.wangshibo.cn:27018" ], "arbiters" : [ "sign-mongo03.wangshibo.cn:27019" ], "setName" : "rs0", "setVersion" : 5, "ismaster" : false, "secondary" : false, "primary" : "sign-mongo02.wangshibo.cn:27018", "arbiterOnly" : true, "me" : "sign-mongo03.wangshibo.cn:27019", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "maxWriteBatchSize" : 1000, "localTime" : ISODate("2017-11-23T02:03:33.874Z"), "maxWireVersion" : 4, "minWireVersion" : 0, "ok" : 1 } rs0:ARBITER> show dbs local 0.000GB arbiter 最為仲裁者,沒有數據副本存儲在本地,能讀取復制集的信息。 ------------------------------------------------------------------------------------------------- 在primary主庫上,創建locs數據庫,該庫的用戶名為locsopr,密碼為locsopr@123 [app@sign-mongo02 ~]$ mongo 172.16.51.217:27018 ...... rs0:PRIMARY> use locs switched to db locs rs0:PRIMARY> db.createUser({user:"locsopr",pwd :"locsopr@123",roles:["readWrite"]}) Successfully added user: { "user" : "locsopr", "roles" : [ "readWrite" ] } rs0:PRIMARY> show users; //查看當前庫下的用戶名 { "_id" : "locs.locsopr", "user" : "locsopr", "db" : "locs", "roles" : [ { "role" : "readWrite", "db" : "locs" } ] } 使用上面的用戶登錄primary主庫 [app@sign-mongo02 ~]$ mongo 172.16.51.217:27018/locs -ulocsopr -plocsopr@123 ......... rs0:PRIMARY> show users { "_id" : "locs.locsopr", "user" : "locsopr", "db" : "locs", "roles" : [ { "role" : "readWrite", "db" : "locs" } ] } rs0:PRIMARY> show dbs admin 0.000GB local 0.000GB rs0:PRIMARY> 再登錄從庫看下: [app@sign-mongo01 ~]$ mongo 172.16.51.216:27017/locs -ulocsopr -plocsopr@123 ....... rs0:SECONDARY> rs.slaveOk(); rs0:SECONDARY> show users { "_id" : "locs.locsopr", "user" : "locsopr", "db" : "locs", "roles" : [ { "role" : "readWrite", "db" : "locs" } ] } rs0:SECONDARY> show dbs admin 0.000GB local 0.000GB
-------------------------------------------------------------------------------------------------------------------------------------------------------
上面介紹的是三台mongodb節點:一主一備一仲裁,這樣,主節點掛了后,通過仲裁機制將primary自動切換到備機上!
如果上面的三台mongodb節點:一主兩備,沒有仲裁節點,那么主節點掛了后,primary會自動切換到其余兩台備節點中的一台上!
一主兩從(端口分別為27017、27018、27019)的mongodb配置和上面一主一從(端口分別為27017、27018)的配置一樣。 多加的一個從節點,在主節點登陸mongodb,使用rs.add命令將這個成員加到集群中即可! 一主兩從的模式,比如: 主節點sign-mongo01.wangshibo.cn的mongodb服務程序掛了后,另外兩個從節點中的一個(比如sign-mongo02.wangshibo.cn)會自動變成primary主節點, 另一個節點sign-mongo03.wangshibo.cn則是新的主節點(sign-mongo02.wangshibo.cn)的從節點。 ++++++++++++++++++++++++++++++如果想讓切換回原來的主節點,做法如下+++++++++++++++++++++++++++++++ 1)恢復原來的主節點sign-mongo01.wangshibo.cn的mongodb服務,使用命令rs.status() 確認數據集成員運行正常。 2)到次節點sign-mongo03.wangshibo.cn中登錄mongodb,運行freeze使其120內不會變為主節點。 > rs.freeze(120) 3) 到新的主節點sign-mongo02.wangshibo.cn中強制切換主節點,stepDown將阻止長事務和寫入操作 > rs.stepDown(120) 4)此時sign-mongo01.wangshibo.cn節點變成primary主節點。使用rs.status()命令可以查看到集群狀態。 +++++++++++++++++++++++若要使某個節點永遠不會變為主節點,設置優先級為0即可+++++++++++++++++++++++ 登陸當前主節點的mongodb,執行下面操作: rs0:PRIMARY> cfg = rs.conf() rs0:PRIMARY> cfg.members[0].priority = 0.5 rs0:PRIMARY> cfg.members[1].priority = 0.5 rs0:PRIMARY> cfg.members[2].priority = 0 rs0:PRIMARY> rs.reconfig(cfg) 說明: 其中成員編號0/1/2為 rs.status()中的 "_id"值 members[2]表示sign-mongo03.wangshibo.cn,則它將永遠不會變成主節點!因為優先級設置為0了! ++++++++++++++++++++++++++++++++移除一個復制成員(兩種方法)++++++++++++++++++++++++++++++++++++ 登陸當前主節點的mongodb,執行下面操作: rs0:PRIMARY> rs.remove("sign-mongo03.wangshibo.cn:27019") rs0:PRIMARY> rs.conf() 或者: rs0:PRIMARY> cfg = rs.conf() rs0:PRIMARY> cfg.members.splice(2,1) rs0:PRIMARY> rs.reconfig(cfg) 移除后到移除的節點服務器(即sign-mongo03.wangshibo.cn),更改配置文件mongod.conf #replSet=rs0 //將這一行注釋 然后再重啟mongodb服務,這就完成了移除(數據庫文件仍保留在當前服務器)。