注:本文基于MongoDB 4.2编写
1 关于replication
MongoDB的副本集能保证数据库服务的冗余性和高可用,这也是生产环境的必要配置。每个副本集都保存相同的数据,保证主节点异常时能接替主节点继续提供服务。
2 搭建集群
我们用3台机器搭建一主二从的集群,MongoDB的安装参考——MongoDB的安装及连接
在三台机器上都安装好MongoDB,然后配置保持一致,
[root@master ~]# grep -vE "^$|^#" /etc/mongod.conf
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
storage:
dbPath: /var/lib/mongo
journal:
enabled: true
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27017
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
3 设置replication
在每台机器的/etc/mongod.conf里增加以下section,也就是指定replication名字,切记这名字在集群host上一定要保持一致
replication:
replSetName: mdbA
添加完记得重启mongod服务,
systemctl restart mongod
4 replication相关操作
这时候我们连接上数据库,查看replication情况,
> rs.status()
{
"ok" : 0,
"errmsg" : "no replset config has been received",
"code" : 94,
"codeName" : "NotYetInitialized"
}
可见此时并没有初始化
4.1 初始化replication
我们选择需要作为主节点的机器上登录数据库,初始化replication,
> rs.initiate({_id:'mdbA',members:[{_id:1,host:'192.168.0.110:27017'}]})
{ "ok" : 1 }
其中mdbA就是我们设置的replication的名字,192.168.0.110就是本机,也就是主节点。
这时我们再查看副本集状态,就能看到我们设置的主节点。
mdbA:OTHER> rs.status()
{
"set" : "mdbA",
"date" : ISODate("2022-03-12T08:13:29.473Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 1,
"writeMajorityCount" : 1,
...
"members" : [
{
"_id" : 1,
"name" : "192.168.0.110:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 920,
"optime" : {
"ts" : Timestamp(1647072799, 8),
"t" : NumberLong(1)
},
...
}
],
...
}
4.2 添加replication
将其余两台host也添加到副本集里,
mdbA:PRIMARY> rs.add( { host: "192.168.0.111:27017", priority: 0, votes: 0 } )
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1647074399, 2),
"signature" : {
"hash" : BinData(0,"Wq3jZwDyUyLirf7YW+AAwH6HQYs="),
"keyId" : NumberLong("7074123805836181509")
}
},
"operationTime" : Timestamp(1647074399, 2)
}
mdbA:PRIMARY> rs.add( { host: "192.168.0.112:27017", priority: 0, votes: 0 } )
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1647074419, 2),
"signature" : {
"hash" : BinData(0,"sECwHoLvoYaS0LNKX6GlAFS9nDA="),
"keyId" : NumberLong("7074123805836181509")
}
},
"operationTime" : Timestamp(1647074419, 2)
}
这时查看replication状态就能看到三台host了,
mdbA:PRIMARY> rs.status()
{
"set" : "mdbA",
"date" : ISODate("2022-03-12T09:34:44.767Z"),
"myState" : 1,
"term" : NumberLong(2),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
...
"members" : [
{
"_id" : 1,
"name" : "192.168.0.110:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 94,
...
},
{
"_id" : 2,
"name" : "192.168.0.111:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 92,
...
},
{
"_id" : 3,
"name" : "192.168.0.112:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 92,
...
}
],
...
}
有一点要注意的是,我添加replication时,设置node的priority和votes都是0,也就说它们并没有选主的权限。这是因为,从节点初始化和同步数据需要一段时间,如果刚好在此时发生选主,如果这些还在同步初始化的节点有投票权,就会有可能无法选主成功。
因此我们开始时将这些新加的node设置无投票权,等到初始化完成,节点变成secondary,再将priority和votes修改为预期值,
mdbA:PRIMARY> var cfg=rs.conf()
mdbA:PRIMARY> cfg.members[1].votes=1
1
mdbA:PRIMARY> cfg.members[1].priority=1
1
mdbA:PRIMARY> rs.reconfig(cfg)
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1647078561, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1647078561, 1)
}
这时再查看节点vote和priority,可以看到已经和primary一致,
mdbA:PRIMARY> rs.conf()
{
"_id" : "mdbA",
"version" : 17,
"protocolVersion" : NumberLong(1),
"writeConcernMajorityJournalDefault" : true,
"members" : [
{
"_id" : 1,
"host" : "192.168.0.110:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
...
"votes" : 1
},
{
"_id" : 2,
"host" : "192.168.0.111:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
...
"votes" : 1
},
{
"_id" : 3,
"host" : "192.168.0.112:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
...
"votes" : 1
}
],
...
}
4.3 删除replication
mdbA:PRIMARY> rs.remove("192.168.0.112:27017")
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1647077843, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1647077843, 1)
}
4.4 强制重新选primary
当我们需要对primary升级时,就需要将主节点切换至其他节点
mdbA:PRIMARY> rs.stepDown()
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1647077892, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1647077892, 1)
}
切换后,再查看副本集状态,可以看到primary已经跑到另一台机器,
mdbA:SECONDARY> rs.status()
{
"set" : "mdbA",
...
"members" : [
{
"_id" : 1,
"name" : "192.168.0.110:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 314,
...
},
{
"_id" : 2,
"name" : "192.168.0.111:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 439,
...
}
],
...
}
参考文档:
- https://docs.mongodb.com/v4.2/replication/
- https://docs.mongodb.com/v4.2/tutorial/expand-replica-set/