ongodb中的复制可以在多台服务器中同步数据。
复制提供了冗余和增加了数据的高可用性,防止单个节点易丢失数据的可能性,也可以用来进行读写分离提高客户端操作性能。
复制集中各节点的mongodb实例有相同的数据集副本。主节点可以接收客户端所有写操作记录到日志中,从库复制主库的操作日志记录应用到其数据库中。
一个客户端只能有一个主节点,如果主节点不可用(10秒内无法连接),复制集中将选一个成员节点作为主节点。
mongodb复制的基本结构如下:
前期 — 配置服务器名称和IP绑定:
1. 设置服务器名称,3台服务器各自设置自己的主机名(当前终端需重新打开才变化)
[root@mongodb11 ~]# vi/etc/sysconfig/network
- HOSTNAME= mongodb11.kk.net
2. 不重启计算机则执行以下方法:
[root@mongodb11 ~]# hostname mongodb11.kk.net
#另两台也更改服务器名称:
HOSTNAME= mongodb12.kk.net
HOSTNAME= mongodb13.kk.net
[root@mongodb12 ~]#hostname mongodb12.kk.net
[root@mongodb13 ~]#hostname mongodb13.kk.net
3. 在3台服务器文件hosts 中都添加以下3行:
# vi /etc/hosts
- 192.168.1.11 mongodb11.kk.net
- 192.168.1.12 mongodb12.kk.net
- 192.168.1.13 mongodb13.kk.net
实验 — 复制的部署测试:
给3台服务器配置mongodb启动参数(参数如下表格):
[root@mongodb ~]# vi/etc/mongod.conf
192.168.1.11(mongodb11.kk.net)#将作为主库 |
pidfilepath=/var/run/mongodb/mongod.pid logpath=/var/log/mongodb/mongod.log dbpath=/var/lib/mongo logappend=true bind_ip=192.168.1.11 port=27017 fork=true replSet=rs0 |
192.168.1.12(mongodb12.kk.net) |
pidfilepath=/var/run/mongodb/mongod.pid logpath=/var/log/mongodb/mongod.log dbpath=/var/lib/mongo logappend=true bind_ip=192.168.1.12 port=27018 fork=true replSet=rs0 |
192.168.1.13(mongodb13.kk.net) |
pidfilepath=/var/run/mongodb/mongod.pid logpath=/var/log/mongodb/mongod.log dbpath=/var/lib/mongo logappend=true bind_ip=192.168.1.13 port=27019 fork=true replSet=rs0 |
配置完成后重启服务:(replSet=rs0 #复制集名称:rs0)
[root@mongodb ~]#service mongod restart
【在192.168.1.11(mongodb11.kk.net)中部署】
1. 登录到mongodb中:
[root@mongodb11 ~]# mongo192.168.1.11:27017
2. 初始化复制集:(集合为:"rs0" ;第一个成员为:"mongodb11.kk.net:27017" )
- >rs.initiate({_id: "rs0",members: [{ _id: 0 , host: "mongodb11.kk.net:27017" }]})
3. 添加另2个成员:
- rs.add("mongodb12.kk.net:27018")
- rs.add("mongodb13.kk.net:27019")
4. 查看成员信息 (或者使用 db.isMaster() )
- rs0:PRIMARY> rs.status()
- {
- "set" : "rs0",
- "date" : ISODate("2015-05-13T13:56:35.020Z"),
- "myState" : 1,
- "members" : [
- {
- "_id" : 0,
- "name" : "mongodb11.kk.net:27017",
- "health" : 1,
- "state" : 1,
- "stateStr" : "PRIMARY",
- "uptime" : 990,
- "optime" : Timestamp(1431525353, 1),
- "optimeDate" : ISODate("2015-05-13T13:55:53Z"),
- "electionTime" : Timestamp(1431524773, 2),
- "electionDate" : ISODate("2015-05-13T13:46:13Z"),
- "configVersion" : 3,
- "self" : true
- },
- {
- "_id" : 1,
- "name" : "mongodb12.kk.net:27018",
- "health" : 1,
- "state" : 2,
- "stateStr" : "SECONDARY",
- "uptime" : 47,
- "optime" : Timestamp(1431525353, 1),
- "optimeDate" : ISODate("2015-05-13T13:55:53Z"),
- "lastHeartbeat" : ISODate("2015-05-13T13:56:33.382Z"),
- "lastHeartbeatRecv" : ISODate("2015-05-13T13:56:34.022Z"),
- "pingMs" : 0,
- "syncingTo" : "mongodb11.kk.net:27017",
- "configVersion" : 3
- },
- {
- "_id" : 2,
- "name" : "mongodb13.kk.net:27019",
- "health" : 1,
- "state" : 2,
- "stateStr" : "SECONDARY",
- "uptime" : 41,
- "optime" : Timestamp(1431525353, 1),
- "optimeDate" : ISODate("2015-05-13T13:55:53Z"),
- "lastHeartbeat" : ISODate("2015-05-13T13:56:33.412Z"),
- "lastHeartbeatRecv" : ISODate("2015-05-13T13:56:33.467Z"),
- "pingMs" : 1,
- "configVersion" : 3
- }
- ],
- "ok" : 1
- }
或者使用该方法查看,结果也是一样:
- use admin
- db.runCommand( { replSetGetStatus : 1 } )
详细说明如下:( 参考: replSetGetStatus ) (state 参考 Replica Set Member States)
- "_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 表示当前节点
5. 测试操作。在主库中,可以任意操作:
- rs0:PRIMARY> show dbs
- admin 0.078GB
- local 4.076GB
- mydb 0.078GB
- test 0.078GB
- rs0:PRIMARY> use mydb
- switched to db mydb
- rs0:PRIMARY>
- rs0:PRIMARY> db.coll.insert({"id":1})
- WriteResult({ "nInserted" : 1 })
- rs0:PRIMARY>
- rs0:PRIMARY> db.coll.find()
- { "_id" : ObjectId("5553670b60be2bf611868985"), "id" : 1 }
- rs0:PRIMARY>
- rs0:PRIMARY> db.coll.remove({"id":1})
- WriteResult({ "nRemoved" : 1 })
- rs0:PRIMARY>
【现在到分库中】
192.168.1.12(mongodb12.kk.net)
192.168.1.13(mongodb13.kk.net)
查看分库数据库目录,发现多了几个数据库,数据库与主库(192.168.1.11)一致!是主库同步过来的。
[root@mongodb12 ~]# ll /var/lib/mongo/
- [root@mongodb13 ~]# ll /var/lib/mongo/
- total 2423844
- -rw-------. 1 mongod mongod 67108864 May 13 21:55 admin.0
- -rw-------. 1 mongod mongod 16777216 May 13 21:55 admin.ns
- drwxr-xr-x. 2 mongod mongod 4096 May 13 21:55 journal
- -rw-------. 1 mongod mongod 67108864 May 13 21:55 local.0
- -rw-------. 1 mongod mongod 2146435072 May 13 23:00 local.1
- -rw-------. 1 mongod mongod 16777216 May 13 23:00 local.ns
- -rwxr-xr-x. 1 mongod mongod 6 May 13 21:40 mongod.lock
- -rw-------. 1 mongod mongod 67108864 May 13 23:00 mydb.0
- -rw-------. 1 mongod mongod 16777216 May 13 23:00 mydb.ns
- -rw-r--r--. 1 mongod mongod 69 May 12 22:05 storage.bson
- -rw-------. 1 mongod mongod 67108864 May 13 21:55 test.0
- -rw-------. 1 mongod mongod 16777216 May 13 21:55 test.ns
- drwxr-xr-x. 2 mongod mongod 4096 May 13 21:55 _tmp
在副本服务器中登录其本地数据库,发现可以连接,但是无法读写操作:
- [root@mongodb12 ~]# mongo 192.168.1.12:27018
- MongoDB shell version: 3.0.2
- connecting to: 192.168.1.12:27018/test
- rs0:SECONDARY>
从库开启读操作(此时可以测试主库插入,从库查看,同步正常):
- rs0:SECONDARY> rs.slaveOk();
现在模拟主库不可用,将主节点服务停止:
[root@mongodb11 ~]# service mongod stop
到节点192.168.1.12 中登录mongodb,查看复制集状态:
- rs0:SECONDARY> rs.status()
- {
- "set" : "rs0",
- "date" : ISODate("2015-05-13T15:44:00.883Z"),
- "myState" : 2,
- "members" : [
- {
- "_id" : 0,
- "name" : "mongodb11.kk.net:27017",
- "health" : 0,
- "state" : 8,
- "stateStr" : "(not reachable/healthy)",
- "uptime" : 0,
- "optime" : Timestamp(0, 0),
- "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
- "lastHeartbeat" : ISODate("2015-05-13T15:43:58.977Z"),
- "lastHeartbeatRecv" : ISODate("2015-05-13T15:42:16.467Z"),
- "pingMs" : 0,
- "lastHeartbeatMessage" : "Failed attempt to connect to mongodb11.kk.net:27017; couldn't connect to server mongodb11.kk.net:27017 (192.168.1.11), connection attempt failed",
- "configVersion" : -1
- },
- {
- "_id" : 1,
- "name" : "mongodb12.kk.net:27018",
- "health" : 1,
- "state" : 2,
- "stateStr" : "SECONDARY",
- "uptime" : 7431,
- "optime" : Timestamp(1431529249, 1),
- "optimeDate" : ISODate("2015-05-13T15:00:49Z"),
- "configVersion" : 3,
- "self" : true
- },
- {
- "_id" : 2,
- "name" : "mongodb13.kk.net:27019",
- "health" : 1,
- "state" : 1,
- "stateStr" : "PRIMARY",
- "uptime" : 6486,
- "optime" : Timestamp(1431529249, 1),
- "optimeDate" : ISODate("2015-05-13T15:00:49Z"),
- "lastHeartbeat" : ISODate("2015-05-13T15:44:00.530Z"),
- "lastHeartbeatRecv" : ISODate("2015-05-13T15:44:00.091Z"),
- "pingMs" : 0,
- "electionTime" : Timestamp(1431531738, 1),
- "electionDate" : ISODate("2015-05-13T15:42:18Z"),
- "configVersion" : 3
- }
- ],
- "ok" : 1
- }
以上信息看到:
原来的主实例(mongodb11.kk.net:27017 )已经无法连接了;
而其中一个节点(mongodb13.kk.net:27019 )变成了主实例,此时该节点是可以读写数据的;
启动服务器 mongodb11.kk.net 的mongodb服务,其变为了副本 (SECONDARY)。
如果想切换回原来的主库,参考:Force a Member to Become Primary
- 1. 使用命令rs.status() 确认数据集成员运行正常
- 2. 到次节点192.168.1.12(mongodb12.kk.net)中登录mongodb,运行freeze使其120内不会变为主节点。
- rs.freeze(120)
- 3. 到主节点192.168.1.13(mongodb13.kk.net)中强制切换主节点,stepDown将阻止长事务和写入操作
- rs.stepDown(120)
- 此时节点192.168.1.11(mongodb11.kk.net)变成主节点
若要使某个节点永远不会变为主节点,设置优先级为0。(参考 Prevent Secondary from Becoming Primary)
- cfg = rs.conf()
- cfg.members[0].priority = 0.5
- cfg.members[1].priority = 0.5
- cfg.members[2].priority = 0
- rs.reconfig(cfg)
- 说明:其中成员编号 0/1/2 为 rs.status()中的 "_id"值
- members[2]为192.168.1.13(mongodb13.kk.net),则它将用于不会变成主节点
移除一个复制成员(两种方法):
- 登录到主库: mongodb11.kk.net ,移除成员
- rs.remove("mongodb13.kk.net:27019")
- 或者:
- cfg = rs.conf()
- cfg.members.splice(2,1)
- rs.reconfig(cfg)
移除后到移除的服务器,更改配置文件 /etc/mongod.conf
#replSet=rs0 #注释
再重启服务,完成移除(数据库文件仍保留在当前服务器)。
更多参考:
Replica Set Deployment Tutorials
Deploy a Replica Set
Deploy a Replica Set for Testing and Development