一、复制集相关概念
复制集 复制是在多台服务器之间同步数据的过程,由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点 Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据 通过上述方式来保持复制集内所有成员存储相同的数据集,提供数据的高可用 复制的目的 Failover (故障转移,故障切换,故障恢复) Redundancy(数据冗余) 避免单点,用于灾难时恢复,报表处理,提升数据可用性 读写分离,分担读压力 对用户透明的系统维护升级 复制集的原理 主节点记录所有的变更到oplog日志 辅助节点(Secondary)复制主节点的oplog日志并且将这些日志在辅助节点进行重放(做) 各个节点之间会定期发送心跳信息,一旦主节点宕机,则触发选举一个新的主节点,剩余的辅助节点指向新的主 10s内各辅助节点无法感知主节点的存在,则开始触发选举 通常1分钟内完成主辅助节点切换,10-30s内感知主节点故障,10-30s内完成选举及切换 复制≠备份 用户恢复数据,防止数据丢失,实现灾难恢复 人为误操作导致数据删除,程序Bug导致数据损坏等 Primary 首要复制节点,由选举产生,提供读写服务的节点,产生oplog日志 Secondary 备用(辅助)复制节点,Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力 在故障时,备用节点可以根据设定的优先级别提升为首要节点。提升了复制集的可用性 Arbiter Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据 Arbiter本身不存储数据,是非常轻量级的服务。 当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性
二、创建复制集
设置环境变量:
[root@server1 ~]# vim ~/.bashrc
[root@server1 ~]# source ~/.bashrc
简述环境变量的区别:
修改方法一
export PATH=/usr/local/mongodb/bin:$PATH
//配置完后可以通过echo $PATH查看配置结果。
生效方法:立即生效
有效期限:临时改变,只能在当前的终端窗口中有效,当前窗口关闭后就会恢复原有的path配置
用户局限:仅对当前用户
修改方法二
通过修改.bashrc文件:
vim ~/.bashrc
//在最后一行添上:
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:(有以下两种)
1、关闭当前终端窗口,重新打开一个新终端窗口就能生效
2、输入“source ~/.bashrc”命令,立即生效
有效期限:永久有效
用户局限:仅对当前用户
修改方法三
通过修改profile文件:
vim /etc/profile
/export PATH //找到设置PATH的行,添加
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:系统重启
有效期限:永久有效
用户局限:对所有用户
创建实例对应的数据目录
[root@server1 ~]# mkdir /home/data/{n1,n2,n3}
[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n1 --logpath /home/data/n1/n1.log --port 27000 --smallfiles --oplogSize 128 --fork
[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n2 --logpath /home/data/n2/n2.log --port 27001 --smallfiles --oplogSize 128 --fork
[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n3 --logpath /home/data/n3/n3.log --port 27002 --smallfiles --oplogSize 128 --fork
查看相应的端口
连接到第一个实例
[root@server1 ~]# mongo localhost:27000
> db.person.insert({name:'cara',age:22}) //提示当前节点非master节点
WriteCommandError({
"operationTime" : Timestamp(0, 0),
"ok" : 0,
"errmsg" : "not master",
"code" : 10107,
"codeName" : "NotMaster",
"$clusterTime" : {
"clusterTime" : Timestamp(0, 0),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
})
//下面我们添加复制集的配置文件
> cfg = {
... '_id':'repSetTest',
... 'members':[
... {'_id':0, 'host': 'localhost:27000'},
... {'_id':1, 'host': 'localhost:27001'},
... {'_id':2, 'host': 'localhost:27002'}
... ]
... }
{
"_id" : "repSetTest",
"members" : [
{
"_id" : 0,
"host" : "localhost:27000"
},
{
"_id" : 1,
"host" : "localhost:27001"
},
{
"_id" : 2,
"host" : "localhost:27002"
}
]
}
>
//复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化 //初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作 //获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。 //通常建议将复制集成员数量设置为奇数,以确保在复制集故障的时候能够正确选举出Primary。 //对于复制集故障导致无法正确选举得到Primary的情形下,复制集将无法提供写服务,处于只读状态
> rs.initiate(cfg) //初始化配置文件
{
"ok" : 1,
"operationTime" : Timestamp(1532397006, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1532397006, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
repSetTest:SECONDARY>
//查看状态,以下提示27000为主节点,其余2个端口为辅助节点
repSetTest:SECONDARY> rs.status()
{
"set" : "repSetTest",
"date" : ISODate("2018-07-24T01:51:46.017Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1532397079, 1),
"members" : [
{
"_id" : 0,
"name" : "localhost:27000",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY", // 主节点
"uptime" : 873,
"optime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-24T01:51:39Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1532397017, 1),
"electionDate" : ISODate("2018-07-24T01:50:17Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "localhost:27001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 99,
"optime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-24T01:51:39Z"),
"optimeDurableDate" : ISODate("2018-07-24T01:51:39Z"),
"lastHeartbeat" : ISODate("2018-07-24T01:51:45.928Z"),
"lastHeartbeatRecv" : ISODate("2018-07-24T01:51:44.242Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "localhost:27000",
"syncSourceHost" : "localhost:27000",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "localhost:27002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 99,
"optime" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1532397099, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-07-24T01:51:39Z"),
"optimeDurableDate" : ISODate("2018-07-24T01:51:39Z"),
"lastHeartbeat" : ISODate("2018-07-24T01:51:45.928Z"),
"lastHeartbeatRecv" : ISODate("2018-07-24T01:51:44.208Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "localhost:27000",
"syncSourceHost" : "localhost:27000",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1532397099, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1532397099, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
//使用isMaster()函数寻找谁是Master
repSetTest:PRIMARY> db.isMaster()
{
"hosts" : [
"localhost:27000",
"localhost:27001",
"localhost:27002"
],
"setName" : "repSetTest",
"setVersion" : 1,
"ismaster" : true,
"secondary" : false,
"primary" : "localhost:27000",
"me" : "localhost:27000",
"electionId" : ObjectId("7fffffff0000000000000001"),
"lastWrite" : {
"opTime" : {
"ts" : Timestamp(1532397409, 1),
"t" : NumberLong(1)
},
"lastWriteDate" : ISODate("2018-07-24T01:56:49Z"),
"majorityOpTime" : {
"ts" : Timestamp(1532397409, 1),
"t" : NumberLong(1)
},
"majorityWriteDate" : ISODate("2018-07-24T01:56:49Z")
},
"maxBsonObjectSize" : 16777216,
"maxMessageSizeBytes" : 48000000,
"maxWriteBatchSize" : 100000,
"localTime" : ISODate("2018-07-24T01:56:59.218Z"),
"logicalSessionTimeoutMinutes" : 30,
"minWireVersion" : 0,
"maxWireVersion" : 7,
"readOnly" : false,
"ok" : 1,
"operationTime" : Timestamp(1532397409, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1532397409, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
//在主复制集上插入文档
repSetTest:PRIMARY> db.replTest.insert({_id:1,value:'abc'})
WriteResult({ "nInserted" : 1 })
查看:
repSetTest:PRIMARY> db.replTest.findOne()
{ "_id" : 1, "value" : "abc" }
//连接到从库查询,提示not master
[root@server1 ~]# mongo localhost:27001
//开启slave查询
repSetTest:SECONDARY> rs.slaveOk(true)
repSetTest:SECONDARY> db.replTest.find()
{ "_id" : 1, "value" : "abc" }
三、复制集自动故障转移
此时,kill掉27000的进程
[root@server1 ~]# kill -9 2098
再次连接会被拒绝
[root@server1 ~]# mongo localhost:27000
MongoDB shell version v4.0.0
connecting to: mongodb://localhost:27000/test
2018-07-24T10:44:22.435+0800 E QUERY [js] Error: couldn't connect to server localhost:27000, connection attempt failed: SocketException: Error connecting to localhost:27000 (127.0.0.1:27000) :: caused by :: Connection refused :
connect@src/mongo/shell/mongo.js:251:13
@(connect):1:6
exception: connect failed
//连接到27001端口,如下面的查询,27000连接失败,27002已经提升为PRIMARY
[root@server1 ~]# mongo localhost:27001
repSetTest:SECONDARY> rs.status()
{
"set" : "repSetTest",
"date" : ISODate("2018-07-24T02:46:40.162Z"),
"myState" : 2,
"term" : NumberLong(2),
"syncingTo" : "localhost:27002",
"syncSourceHost" : "localhost:27002",
"syncSourceId" : 2,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
},
"appliedOpTime" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
},
"durableOpTime" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1532400376, 1),
"members" : [
{
"_id" : 0,
"name" : "localhost:27000",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)", // 提示27001不可到达
"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-07-24T02:46:40.048Z"),
"lastHeartbeatRecv" : ISODate("2018-07-24T02:43:56.679Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Error connecting to localhost:27000 (127.0.0.1:27000) :: caused by :: Connection refused",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : -1
},
{
"_id" : 1,
"name" : "localhost:27001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 4137,
"optime" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-07-24T02:46:36Z"),
"syncingTo" : "localhost:27002",
"syncSourceHost" : "localhost:27002",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 2,
"name" : "localhost:27002",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY", //27002成为PRIMARY
"uptime" : 3391,
"optime" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1532400396, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-07-24T02:46:36Z"),
"optimeDurableDate" : ISODate("2018-07-24T02:46:36Z"),
"lastHeartbeat" : ISODate("2018-07-24T02:46:39.798Z"),
"lastHeartbeatRecv" : ISODate("2018-07-24T02:46:38.901Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1532400244, 1),
"electionDate" : ISODate("2018-07-24T02:44:04Z"),
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1532400396, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1532400396, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
//重新启动27000实例
[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n1 --logpath /home/data/n1/n1.log --port 27000 --smallfiles --oplogSize 128 --fork
2018-07-24T10:51:56.523+0800 I CONTROL [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'
about to fork child process, waiting until server is ready for connections.
forked process: 2598
child process started successfully, parent exiting
//再次查看复制集的状态,此时27000为辅助副本
"members" : [
{
"_id" : 0,
"name" : "localhost:27000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY", // 此节点变成了辅助节点
"uptime" : 28,
"optime" : {
"ts" : Timestamp(1532400746, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1532400746, 1),
"t" : NumberLong(2)
},
四、获取复制集的帮助
//获取副本集相关的帮助命令
repSetTest:SECONDARY> rs.help()
rs.status() { replSetGetStatus : 1 } checks repl set status
rs.initiate() { replSetInitiate : null } initiates set with default settings
rs.initiate(cfg) { replSetInitiate : cfg } initiates set with configuration cfg
rs.conf() get the current configuration object from local.system.replset
rs.reconfig(cfg) updates the configuration of a running replica set with cfg (disconnects)
rs.add(hostportstr) add a new member to the set with default attributes (disconnects)
rs.add(membercfgobj) add a new member to the set with extra attributes (disconnects)
rs.addArb(hostportstr) add a new member which is arbiterOnly:true (disconnects)
rs.stepDown([stepdownSecs, catchUpSecs]) step down as primary (disconnects)
rs.syncFrom(hostportstr) make a secondary sync from the given member
rs.freeze(secs) make a node ineligible to become primary for the time specified
rs.remove(hostportstr) remove a host from the replica set (disconnects)
rs.slaveOk() allow queries on secondary nodes
rs.printReplicationInfo() check oplog size and time range
rs.printSlaveReplicationInfo() check replica set members and replication lag
db.isMaster() check who is primary
reconfiguration helpers disconnect from the database so the shell will display
an error, even if the command succeeds.
repSetTest:SECONDARY>