mongodb副本集简介
MongoDB 副本集(Replica Set)包括主节点(primary)跟副本节点(Secondaries)。主节点只能有一个,所有的写操作请求都在主节点上面处理。副本节点可以有多个,通过同步主节点的操作日志(oplog)来备份主节点数据。
在主节点挂掉后,有选举权限的副本节点会自动发起选举,并从中选举出新的主节点。副本节点可以通过配置,指定其具体的属性,比如选举、隐藏、延迟同步等,最多可以有50个副本节点,但只能有7个副本节点能参与选举。虽然副本节点不能处理写操作,但可以处理读请求。
搭建一个副本集集群最少需要三个节点:一个主节点,两个备份节点,三个节点的架构如下图所示:
如果只有一个主节点,一个副本节点,且没有资源拿来当第二个副本节点,那就可以起一个仲裁者节点(arbiter),不存数据,只用来选举用,如下图所示:
当主节点挂掉后,那么两个副本节点会进行选举,从中选举出一个新的主节点,流程如下:
mongodb副本集部署
搭建一套3节点副本集(1个 Primary 节点,2个 Secondary 节点),节点规划如下:
主机名 | IP地址 | 角色 |
mongodb01 | 192.168.92.80 | primary |
mongodb02 | 192.168.92.81 | secondary |
mongodb03 | 192.168.92.82 | secondary |
3个节点分别配置主机名
hostnamectl set-hostname mongodb01
hostnamectl set-hostname mongodb02
hostnamectl set-hostname mongodb03
3个节点分别配置主机名解析
cat > /etc/hosts <<EOF
192.168.92.80 mongodb01
192.168.92.81 mongodb02
192.168.92.82 mongodb03
EOF
关闭防火墙和selinux
systemctl disable --now firewalld
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config && setenforce 0
配置时间同步
yum install -y chrony
systemctl enable --now chronyd
配置国内yum源
cat > /etc/yum.repos.d/mongodb.repo <<'EOF'
[mongodb-org]
name=MongoDB Repository
baseurl=https://mirrors.tuna.tsinghua.edu.cn/mongodb/yum/el$releasever/
gpgcheck=0
enabled=1
EOF
3个节点安装mongodb
yum install -y mongodb-org
修改mongodb配置文件,增加以下配置
# cat /etc/mongod.conf
...
replication:
replSetName: "rs0"
net:
bindIp: 0.0.0.0
...
启动mongodb服务
systemctl enable --now mongod
使用 mongo 进入第一个实例mongodb01:
[root@mongodb01 ~]# mongo
使用 rs.initiate() 进行初始化
rs.initiate( {
_id : "rs0",
members: [
{ _id: 0, host: "mongodb01:27017" },
{ _id: 1, host: "mongodb02:27017" },
{ _id: 2, host: "mongodb03:27017" }
]
})
以上就已经完成了一个副本集的搭建,在 mongo shell 中执行 rs.conf() 可以看到每个节点中 host、arbiterOnly、hidden、priority、 votes、slaveDelay等属性。
rs0:PRIMARY> rs.conf()
{
"_id" : "rs0",
"version" : 1,
"term" : 1,
"protocolVersion" : NumberLong(1),
"writeConcernMajorityJournalDefault" : true,
"members" : [
{
"_id" : 0,
"host" : "mongodb01:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "mongodb02:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "mongodb03:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : -1,
"catchUpTakeoverDelayMillis" : 30000,
"getLastErrorModes" : {
},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("6062b2aa526a897f92fa34bc")
}
}
或者使用以下命令确认primary节点:
db.isMaster()
确保副本集具有主副本集,使用rs.status()来识别副本集的主。
rs0:PRIMARY> rs.status()
......
"members" : [
{
"_id" : 0,
"name" : "mongodb01:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 938,
"optime" : {
"ts" : Timestamp(1617081783, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2021-03-30T05:23:03Z"),
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1617081013, 1),
"electionDate" : ISODate("2021-03-30T05:10:13Z"),
"configVersion" : 1,
"configTerm" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "mongodb02:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 781,
"optime" : {
"ts" : Timestamp(1617081783, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1617081783, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2021-03-30T05:23:03Z"),
"optimeDurableDate" : ISODate("2021-03-30T05:23:03Z"),
"lastHeartbeat" : ISODate("2021-03-30T05:23:04.172Z"),
"lastHeartbeatRecv" : ISODate("2021-03-30T05:23:03.059Z"),
"pingMs" : NumberLong(2),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "mongodb01:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"configTerm" : 1
},
{
"_id" : 2,
"name" : "mongodb03:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 781,
"optime" : {
"ts" : Timestamp(1617081783, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1617081783, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2021-03-30T05:23:03Z"),
"optimeDurableDate" : ISODate("2021-03-30T05:23:03Z"),
"lastHeartbeat" : ISODate("2021-03-30T05:23:04.167Z"),
"lastHeartbeatRecv" : ISODate("2021-03-30T05:23:03.215Z"),
"pingMs" : NumberLong(2),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "mongodb01:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"configTerm" : 1
}
],
......
mongodb副本集故障转移
可以直接停掉主节点mongodb01来测试下主节点挂掉后,副本节点重新选举出新的主节点,即自动故障转移(Automatic Failover)。
[root@mongodb01 ~]# systemctl stop mongod
杀掉主节点 mongodb01后,可以看到 mongodb02 的输出日志里面选举部分,已经发起选举,并成功参选成为主节点:
[root@mongodb02 ~]# cat /var/log/mongodb/mongod.log |grep PRIMARY
{"t":{"$date":"2021-03-30T13:10:13.173+08:00"},"s":"I", "c":"REPL", "id":21215, "ctx":"ReplCoord-0","msg":"Member is in new state","attr":{"hostAndPort":"mongodb01:27017","newState":"PRIMARY"}}
{"t":{"$date":"2021-03-30T13:27:51.523+08:00"},"s":"I", "c":"REPL", "id":21358, "ctx":"ReplCoord-11","msg":"Replica set state transition","attr":{"newState":"PRIMARY","oldState":"SECONDARY"}}
然后执行 rs.status() 查看当前副本集情况
rs.status().members.forEach(
function(z){
printjson(z.name);
printjson(z.stateStr);
}
)
可以看到mongodb02变为主节点,mongodb01显示已挂掉。
"mongodb01:27017"
"(not reachable/healthy)"
"mongodb02:27017"
"PRIMARY"
"mongodb03:27017"
"SECONDARY"
再次启动mongodb01:
[root@mongodb01 ~]# systemctl start mongod
可以看到已检测到 mongodb01,并且已变为副本节点,通过rs.status 查看结果也是如此。
"mongodb01:27017"
"SECONDARY"
"mongodb02:27017"
"PRIMARY"
"mongodb03:27017"
"SECONDARY"
从副本集删除成员
有2种方法从副本集中删除成员,使用rs.remove()或rs.reconfig()。
使用rs.remove()删除成员
1、关闭您要删除的mongod成员的实例,以删除mongodb03成员为例:
[root@mongodb03 ~]# mongo
rs0:SECONDARY> use admin;
rs0:SECONDARY> db.shutdownServer()
2、连接到副本集的当前primary,可以连接到任意副本集成员使用db.isMaster()
确定当前的主数据库。使用rs.remove()以下两种形式之一删除成员:
[root@mongodb02 ~]# mongo
rs0:PRIMARY> rs.remove("mongodb03:27017")
rs0:PRIMARY> rs.remove("mongodb03")
3、使用以下命令查看集群信息
rs.status().members.forEach(
function(z){
printjson(z.name);
printjson(z.stateStr);
}
)
确认成功移除mongodb03节点
"mongodb01:27017"
"SECONDARY"
"mongodb02:27017"
"PRIMARY"
使用rs.reconfig()删除成员
使用rs.conf() 确定要删除的节点位置
[root@mongodb02 ~]# mongo
rs0:PRIMARY> rs.conf()
删除成员
rs0:PRIMARY> cfg = rs.conf()
rs0:PRIMARY> cfg.members.splice(2,1)
rs0:PRIMARY> rs.reconfig(cfg)
将成员添加到副本集
1、启动新mongod实例,启动时需要在命令行指定副本集名称,这里通过在配置文件中配置:
# cat /etc/mongod.conf
...
replication:
replSetName: "rs0"
...
清空数据并重新启动
[root@mongodb03 ~]# rm -rf /var/lib/mongo/*
[root@mongodb03 ~]# systemctl restart mongod
2、添加成员到副本集
连接到副本集的主数据库,您只能在连接到主要成员时添加成员:
[root@mongodb02 ~]# mongo
rs0:PRIMARY> rs.add( { host: "mongodb03:27017", priority: 0, votes: 0 } )
3、重新配置优先级
一旦新加入的成员已经过渡到 SECONDARY状态,如果需要的话,使用rs.reconfig()更新新添加的成员priority和votes。
使用rs.conf()查看新成员id,则将其priority和votes更新为 1,请使用以下操作序列:
var cfg = rs.conf();
cfg.members[2].priority = 1
cfg.members[2].votes = 1
rs.reconfig(cfg)