MongoDB 副本集把SECONDARY提升为PRIMARY


事故背景

​ 线上环境有一个MongoDB副本集,由于是部署在客户那边本地机房,客户误操作把部署副本集的另外2个节点的 VM 给删除了(并且VM已经无法恢复了)。所幸的是还有一个节点存活,登录节点后发现这个节点是 SECONDARY,所以可能会有一部分数据丢失,而且此时已经无法对应用提供读写服务。此时只能停服维护,并对集群进行恢复。

​ 基于以上问题,下面对副本集恢复操作步鄹进行了记录。

 

处理思路

  1. 对mongodb数据进行备份(防止恢复集群时出现意外导致数据丢失)。
  2. 把仅存的 SECONDARY 节点提升为 PRIMARY,删除集群中另外2个不存活的节点,然后重新配置MongoDB副本集。
  3. 新部署2个MongoDB节点,并加入到集群中。
  4. 等待 PRIMARY 节点数据同步到另外2个新节点后,进行数据验证,结束生产环境维护。

注意:

由于原先的集群中只存有 SECONDARY 节点,PRIMARY 节点已经丢失,所以存在部署数据没同步到 SECONDARY 的可能。但由于PRIMARY节点的VM已经被删,这部分未同步的数据的丢失在所难免,想恢复这部分数据只能根据自己的业务、代码逻辑设定才有补上丢失的数据的可能性。

 

集群恢复

1、在SECONDARY节点删除挂掉的primary节点

1.1 查看当前副本集配置

rs1:SECONDARY> rs.conf()

输出内容:

rs1:SECONDARY> use admin
switched to db admin
rs1:SECONDARY> rs_conf = rs.config() { "_id" : "rs1", "version" : 7, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "192.168.30.207:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.30.213:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "192.168.30.214: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("5f5094994a4d5004eae73e2f") } }

1.2 删除集群成员

  • 比如要删除members中 host 为 192.168.30.213:27017 的成员,通过rs.conf()找到成员的 _id
{
    "_id" : 1, "host" : "192.168.30.213:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 },
  • 删除 _id 为1的成员

splice的第一个参数表示要删除的数组元素的下标

0 表示集群中成员节点的 "_id"

1 表示删除的个数

rs1:SECONDARY> rs_conf = rs.conf()
rs1:SECONDARY> rs_conf.members.splice(0,1)

输出内容:

rs1:SECONDARY> rs_conf.members.splice(1,1)
[
    {
        "_id" : 1, "host" : "192.168.30.213:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ]

依照此方法删除副本集中不存活的节点。

注意:

有一点需要注意,由于已经删除了 _id 为1的成员,所以后面的成员的 _id 号都会减小1,与数组中元素的下标相同。

 

2、重新配置MongoDB副本集

2.1 重置集群配置

rs_conf 就是上面修改后的配置,加force参数是因为 SECONDARY 默认没有执行此命令的权限

rs1:SECONDARY> rs.reconfig(rs_conf, {"force":true})

返回内容:

rs1:SECONDARY> rs.reconfig(rs_conf, {"force":true}) { "ok" : 1, "operationTime" : Timestamp(1619586716, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619588924, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs1:PRIMARY> 

2.2 查看集群状态

rs1:PRIMARY> rs.status()

返回内容:

{
    "set" : "rs1", "date" : ISODate("2021-04-28T05:51:03.672Z"), "myState" : 1, "term" : NumberLong(17), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "appliedOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "durableOpTime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) } }, "members" : [ { "_id" : 0, "name" : "192.168.30.207:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 7482, "optime" : { "ts" : Timestamp(1619589055, 1), "t" : NumberLong(17) }, "optimeDate" : ISODate("2021-04-28T05:50:55Z"), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1619588924, 1), "electionDate" : ISODate("2021-04-28T05:48:44Z"), "configVersion" : 124340, "self" : true, "lastHeartbeatMessage" : "" } ], "ok" : 1, "operationTime" : Timestamp(1619589055, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619589055, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }

此时发现,这个 SECONDARY 节点已经提升为 PRIMARY,并且集群状态中,也就只有我们当前一个节点。

接下来就可以向副本集中添加新的MongoDB节点了。

 

3、添加新的MongoDB节点

这里省略新节点的部署过程,具体可以参考[《MongoDB 单节点升级为副本集高可用集群》](MongoDB 单节点升级为副本集高可用集群 - HEBIN博客 (wanhebin.com))文章中MongoDB节点部署的步鄹。

注意:

向mongodb副本集添加实例后,PRIMARY节点数据能够自动同步到新添加的SECONDARY节点,无需人工干预。

3.1 增加实例

登录PRIMARY节点,添加MongoDB实例。

新添加的实例优先级权重默认为1,如需调整,建议等数据同步完成后进行权重更改。

rs1:PRIMARY> use admin
rs1:PRIMARY> rs.add('192.168.30.213:27017') rs1:PRIMARY> rs.add('192.168.30.214:27017')

添加节点的返回结果如下:

rs1:PRIMARY> rs.add('192.168.30.213:27017') { "ok" : 1, "operationTime" : Timestamp(1619581966, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619581966, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs1:PRIMARY> rs.add('192.168.30.214:27017') { "ok" : 1, "operationTime" : Timestamp(1619581975, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619581975, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }

3.2 删除实例

如果添加错节点时,可以通过 rs.remove() 来删除错误的节点(因为此时当前实例已经是 PRIMARY 了,所以不需要用 1.2 中方法剔除节点)。

从mongodb副本集中移除实例,不可移除primary

rs1:PRIMARY> use admin
rs1:PRIMARY> rs.remove('192.168.30.214:27017')

返回内容:

rs1:PRIMARY> rs.remove('192.168.30.213:27017') { "ok" : 1, "operationTime" : Timestamp(1619581713, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619581713, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs1:PRIMARY> rs.remove('192.168.30.214:27017') { "ok" : 1, "operationTime" : Timestamp(1619581777, 2), "$clusterTime" : { "clusterTime" : Timestamp(1619581777, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }

注意:

副本集经过添加删除后顺序会乱,可以根据需要设置权重来调整。

 

4、调整节点权重

如果想在集群宕机恢复后,还想让某一节点始终保持为 PRIMARY,可以把此节点的权重设置成最大。

4.1 设置权重

找到对应节点在副本集中成员_id,进行权重设置。

这里以成员0为例,其host为192.168.30.207:27017

rs1:PRIMARY> rs_conf = rs.config()
rs1:PRIMARY> rs_conf.members[0].priority=10

4.2 生效配置

rs1:PRIMARY> rs.reconfig(rs_conf)

返回结果:

rs1:PRIMARY> rs.reconfig(rs_conf)
{
    "ok" : 1, "operationTime" : Timestamp(1619591404, 1), "$clusterTime" : { "clusterTime" : Timestamp(1619591404, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }

4.3 验证权重配置

  • 查询成员0的权重
rs1:PRIMARY> rs.config()

返回内容:

{
        "_id" : 0, "host" : "192.168.30.207:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 10, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }
  • 模拟宕机恢复后的集群状态

关闭三个节点的mongodb服务,再无序恢复,然后连接进节点192.168.30.207:27017,成员0依然还是PRIMARY。(为了必然偶然性,可以进行多次测试)

 

https://www.wanhebin.com/database/mongodb/1026.html

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM