复制集(replica Set)或者副本集是MongoDB的核心高可用特性之一,它基于主节点的oplog日志持续传送到辅助节点,并重放得以实现主从节点一致。再结合心跳机制,当感知到主节点不可访问或宕机的情形下,辅助节点通过选举机制来从剩余的辅助节点中推选一个新的主节点从而实现自动切换。对于一个已经存在的MongoDB Replica Set集群,可以对其进行节点的增加,删除,以及修改节点属性等等。本文即是围绕这些进行描述。
有关MongoDB复制集概念及其搭建,可以参考:MongoDB 复制集(Replica Set)
一、节点的移除
//当前的演示环境
repSetTest:PRIMARY> db.version()
3.2.11
//主从节点
PRIMARY: localhost:27001
SECONDARY: localhost:27000
SECONDARY: localhost:27002
repSetTest:PRIMARY> rs.remove("localhost:27000")
{ "ok" : 1 }
//移除节点后的状态信息
repSetTest:PRIMARY> rs.status()
{
"set" : "repSetTest",
"date" : ISODate("2016-08-30T05:48:13.010Z"),
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "localhost:27001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 526,
"optime" : Timestamp(1472536085, 1),
"optimeDate" : ISODate("2016-08-30T05:48:05Z"),
"electionTime" : Timestamp(1472535890, 1),
"electionDate" : ISODate("2016-08-30T05:44:50Z"),
"configVersion" : 2,
"self" : true
},
{
"_id" : 2,
"name" : "localhost:27002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 426,
"optime" : Timestamp(1472536085, 1),
"optimeDate" : ISODate("2016-08-30T05:48:05Z"),
"lastHeartbeat" : ISODate("2016-08-30T05:48:11.805Z"),
"lastHeartbeatRecv" : ISODate("2016-08-30T05:48:12.877Z"),
"pingMs" : 0,
"syncingTo" : "localhost:27001",
"configVersion" : 2
}
],
"ok" : 1
}
//移除后查看配置文件
//此时版本version为2,只有2个节点
repSetTest:PRIMARY> rs.config()
{
"_id" : "repSetTest",
"version" : 2,
"members" : [
{
"_id" : 1,
"host" : "localhost:27001",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : 0,
"votes" : 1
},
{
"_id" : 2,
"host" : "localhost:27002",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : 0,
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatTimeoutSecs" : 10,
"getLastErrorModes" : {
},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
}
}
}
二、节点的增加
repSetTest:PRIMARY> rs.add("localhost:27000")
{ "ok" : 1 }
repSetTest:PRIMARY> rs.status()
{
"set" : "repSetTest",
"date" : ISODate("2016-08-30T05:50:56.678Z"),6
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "localhost:27001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 689,
"optime" : Timestamp(1472536231, 1),
"optimeDate" : ISODate("2016-08-30T05:50:31Z"),
"electionTime" : Timestamp(1472535890, 1),
"electionDate" : ISODate("2016-08-30T05:44:50Z"),
"configVersion" : 3,
"self" : true
},
{
"_id" : 2,
"name" : "localhost:27002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 590,
"optime" : Timestamp(1472536231, 1),
"optimeDate" : ISODate("2016-08-30T05:50:31Z"),
"lastHeartbeat" : ISODate("2016-08-30T05:50:55.336Z"),
"lastHeartbeatRecv" : ISODate("2016-08-30T05:50:55.063Z"),
"pingMs" : 0,
"configVersion" : 3
},
{
"_id" : 3,
"name" : "localhost:27000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY", //增加后的节点此时作为一个从节点
"uptime" : 23,
"optime" : Timestamp(1472536231, 1),
"optimeDate" : ISODate("2016-08-30T05:50:31Z"),
"lastHeartbeat" : ISODate("2016-08-30T05:50:55.342Z"),
"lastHeartbeatRecv" : ISODate("2016-08-30T05:50:55.341Z"),
"pingMs" : 0,
"configVersion" : 3
}
],
"ok" : 1
}
三、启用Arbiter节点
repSetTest:PRIMARY> rs.remove("localhost:27000")
{ "ok" : 1 }
repSetTest:PRIMARY> rs.add({host:"localhost:27000",arbiterOnly:true})
{ "ok" : 1 }
repSetTest:PRIMARY> rs.config()
{
"_id" : "repSetTest",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "localhost:27001",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false, // Author : Leshami
"priority" : 1, // Blog : http://blog.csdn.net/leshami
"tags" : {
},
"slaveDelay" : 0,
"votes" : 1
},
{
"_id" : 2,
"host" : "localhost:27002",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : 0,
"votes" : 1
},
{
"_id" : 3,
"host" : "localhost:27000",
"arbiterOnly" : true, //此处表明当前结点为仲裁节点
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : 0,
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatTimeoutSecs" : 10,
"getLastErrorModes" : {
},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
}
}
}
对于Arbiter也可以使用rs.addArb函数来添加
如:> rs.addArb("localhost:27000")
验证仲裁节点数据写入
repSetTest:PRIMARY> use tempdb
switched to db tempdb
repSetTest:PRIMARY> db.users.insert({id:1,ename:"robin"})
WriteResult({ "nInserted" : 1 })
# mongo localhost:27000
MongoDB shell version: 3.0.12
connecting to: localhost:27000/test
repSetTest:ARBITER> show dbs;
2016-08-30T14:26:26.753+0800 E QUERY Error: listDatabases failed:
{ "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" }
at Error (<anonymous>)
at Mongo.getDBs (src/mongo/shell/mongo.js:47:15)
at shellHelper.show (src/mongo/shell/utils.js:630:33)
at shellHelper (src/mongo/shell/utils.js:524:36)
at (shellhelp2):1:1 at src/mongo/shell/mongo.js:47
repSetTest:ARBITER> rs.slaveOk(true)
repSetTest:ARBITER> show dbs; //执行该命令,看不到tempdb
local 0.281GB
test 0.031GB
repSetTest:ARBITER> use tempdb
switched to db tempdb
repSetTest:ARBITER> db.users.count() //执行count,提示节点正在恢复
2016-08-30T14:30:04.571+0800 E QUERY Error: count failed:
{ "note" : "from execCommand", "ok" : 0, "errmsg" : "node is recovering" }
at Error (<anonymous>)
at DBQuery.count (src/mongo/shell/query.js:326:11)
at DBCollection.count (src/mongo/shell/collection.js:1046:27)
at (shell):1:10 at src/mongo/shell/query.js:326
四、设定节点的优先级别(Priority)
优先级用于确定一个倾向成为主节点的程度。取值范围为0-100
Priority 0节点的选举优先级为0,不会被选举为Primary,这样的成员称为被动成员
对于跨机房复制集的情形,如A,B机房,最好将『大多数』节点部署在首选机房,以确保能选择合适的Primary
对于Priority为0节点的情况,通常作为一个standby,或由于硬件配置较差,设置为0以使用不可能成为主
//如下示例,在新增节点的时候设定该节点的优先级别
repSetTest:PRIMARY> rs.add({"_id":3,"host":"localhost:27000","priority":1.5})
也可以通过下面的方式修改优先级别
repSetTest:PRIMARY> var config=rs.config()
repSetTest:PRIMARY> config.members[2].priority=2
2
repSetTest:PRIMARY> rs.reconfig(config)
{ "ok" : 1 }
五、投票节点(Vote)
投票节点不保存数据副本,不可能成为主节点
Mongodb 3.0里,复制集成员最多50个,参与Primary选举投票的成员最多7个
对于超出7个的其他成员(Vote0)的vote属性必须设置为0,即不参与投票
六、隐藏节点(Hidden)
Hidden节点不能被选为主(Priority为0),并且对Driver不可见。
因Hidden节点不会接受Driver的请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响复制集的服务
隐藏节点成员建议总是将其优先级设置为0(priority 0)
由于对Driver不可见,因此不会作为read preference节点,隐藏节点可以作为投票节点
在分片集群当中,mongos不会同隐藏节点交互
> cfg = rs.conf()
> cfg.members[2].priority = 0
> cfg.members[2].hidden = true
> rs.reconfig(cfg)
查看设置为隐藏阶段后的属性
repSetTest:SECONDARY> db.isMaster()
{
"hosts" : [
"localhost:27000",
"localhost:27001"
],
"setName" : "repSetTest",
"setVersion" : 2,
"ismaster" : false,
"secondary" : true,
"primary" : "localhost:27000",
"passive" : true,
"hidden" : true, //此处表明当前节点为隐藏节点
"me" : "localhost:27002",
"maxBsonObjectSize" : 16777216,
"maxMessageSizeBytes" : 48000000,
"maxWriteBatchSize" : 1000,
"localTime" : ISODate("2017-03-06T10:15:48.257Z"),
"maxWireVersion" : 4,
"minWireVersion" : 0,
"ok" : 1
}
七、延迟节点(Delayed)
延迟节点包含复制集的部分数据,是复制集数据的子集
延迟节点上的数据通常落后于Primary一段时间(可配置,比如1个小时)。
当人为错误或者无效的数据写入Primary时,可通过Delayed节点的数据进行回滚
延迟节点的要求:
优先级别为0(priority 0),避免参与primary选举
应当设置为隐藏节点(以避免应用程序查询延迟节点)
可以作为一个投票节点,设置 members[n].votes 值为1
延迟节点注意事项:
延迟时间应当等于和大于维护窗口持续期
应当小于oplog容纳数据的时间窗口
> cfg = rs.conf()
> cfg.members[2].priority = 0
> cfg.members[2].hidden = true
> cfg.members[2].slaveDelay = 3600
> rs.reconfig(cfg)
repSetTest:SECONDARY> db.isMaster()
{
"hosts" : [
"localhost:27000",
"localhost:27001"
],
"setName" : "repSetTest",
"setVersion" : 3,
"ismaster" : false,
"secondary" : true,
"primary" : "localhost:27000",
"passive" : true,
"hidden" : true,
"slaveDelay" : 3600, //此处表面当前节点具有延迟属性,为延迟节点
"me" : "localhost:27002",
"maxBsonObjectSize" : 16777216,
"maxMessageSizeBytes" : 48000000,
"maxWriteBatchSize" : 1000,
"localTime" : ISODate("2017-03-06T10:19:57.148Z"),
"maxWireVersion" : 4,
"minWireVersion" : 0,
"ok" : 1
}