【问题标题】:Endless recovering state of secondary二次元无休止的恢复状态
【发布时间】:2025-12-24 00:05:18
【问题描述】:

我在 MongoDB 3.0.2 上构建了一个包含一个主、一个辅助和一个仲裁器的复制集。主节点和仲裁节点在同一台主机上,而从节点在另一台主机上。

随着写过载的增加,Secondary无法跟随Primary步入recovering状态。主服务器可以连接到辅助服务器,因为我可以通过主服务器上的 Mongo shell 登录到辅助服务器。

我停止所有操作并使用命令rs.status() 观察辅助节点的状态,然后在辅助节点上键入命令rs.syncFrom("primary's ip:port")

然后rs.status()命令的结果显示secondary的optimeDate远远落后于primary,间歇性出现一条消息如下:

"set" : "shard01", "date" : ISODate("2015-05-15T02:10:55.382Z"), "myState" : 3, "members" : [ { "_id" : 0, "name" : "xxx.xxx.xxx.xxx:xxx", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 135364, "optime" : Timestamp(1431655856, 6), "optimeDate" : ISODate("2015-05-15T02:10:56Z"), "lastHeartbeat" : ISODate("2015-05-15T02:10:54.306Z"), "lastHeartbeatRecv" : ISODate("2015-05-15T02:10:53.634Z"), "pingMs" : 0, "electionTime" : Timestamp(1431520398, 2), "electionDate" : ISODate("2015-05-13T12:33:18Z"), "configVersion" : 3 }, { "_id" : 1, "name" : "xxx.xxx.xxx.xxx:xxx", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 135364, "lastHeartbeat" : ISODate("2015-05-15T02:10:53.919Z"), "lastHeartbeatRecv" : ISODate("2015-05-15T02:10:54.076Z"), "pingMs" : 0, "configVersion" : 3 }, { "_id" : 2, "name" : "xxx.xxx.xxx.xxx:xxx", "health" : 1, "state" : 3, "stateStr" : "RECOVERING", "uptime" : 135510, "optime" : Timestamp(1431602631, 134), "optimeDate" : ISODate("2015-05-14T11:23:51Z"), "infoMessage" : "could not find member to sync from", "configVersion" : 3, "self" : true } ], "ok" : 1

"infoMessage" : "找不到要同步的成员"

主节点和仲裁节点都OK。我想知道此消息的原因以及如何将辅助的状态从“正在恢复”更改为“辅助”。

【问题讨论】:

  • 请发布rs.status() 的输出并尝试从三个有问题的辅助设备连接到主设备上为mongod 配置的端口。
  • 我无法附加图片,rs.status() 的输出如下所示:"stateStr" : "PRIMARY", "optimeDate" : ISODate("2015-05-15T06:32:52Z")," "stateStr" : "RECOVERING", "optimeDate" : ISODate("2015-05-14T11 :23:51Z")
  • 请编辑您的问题并将rs.status() 的输出放在那里,以便人们更容易获得有关您问题的更多信息。我同意@MarkusWMahlberg 的观点,你首先应该确保主次元之间没有连接问题。

标签: mongodb replication


【解决方案1】:

问题(很可能)

primary 上的最后一个操作来自“2015-05-15T02:10:56Z”,而将要从的最后一个操作来自“2015-05-14T11:23:51Z”,这是一个相差约15小时。该窗口很可能超过您的复制 oplog 窗口(您的 oplog 中第一个和最后一个操作条目的时间之间的差异)。简而言之,主节点上的操作太多,辅助节点赶不上。

更详细一点(虽然简化了):在初始同步期间,辅助同步的数据是给定时间点的数据。当那个时间点的数据被同步过来时,从节点连接到 oplog 并根据 oplog 条目应用在所述时间点和现在之间所做的更改。只要 oplog 保存提到的时间点之间的所有操作,这就会很好地工作。但是 oplog 的大小是有限的(它是所谓的capped collection)。因此,如果主节点上发生的操作多于 oplog 在初始同步期间可以容纳的操作,则最旧的操作会“淡出”。辅助节点认识到并非所有操作都可用于“构建”与主节点相同的数据,因此拒绝完成同步,保持RECOVERY 模式。

解决方案

这个问题是一个已知问题,而不是错误,而是 MongoDB 内部工作原理和开发团队做出的几个故障安全假设的结果。因此,有几种方法可以处理这种情况。遗憾的是,由于您只有两个数据承载节点,都涉及停机时间。

选项 1:增加 oplog 大小

这是我的首选方法,因为它一次性解决问题,并且(某种程度)一劳永逸。不过,它比其他解决方案要复杂一些。从高层次的角度来看,这些是您要采取的步骤。

  1. 关闭主节点
  2. 使用直接访问数据文件创建 oplog 备份
  3. 在独立模式下重启mongod
  4. 将当前 oplog 复制到临时集合中
  5. 删除当前操作日志
  6. 重新创建所需大小的 oplog
  7. 将 oplog 条目从临时集合复制回闪亮的新 oplog
  8. 作为副本集的一部分重新启动mongod

在进行初始同步之前不要忘记增加辅助节点的 oplog,因为它可能在将来的某个时间成为主节点!

详情请阅读"Change the size of the oplog" in the tutorials regarding replica set maintenance

选项 2:在同步期间关闭应用

如果选项 1 不可行,唯一真正的其他解决方案是关闭导致副本集负载的应用程序,重新启动同步并等待它完成。根据要传输的数据量,用几个小时计算。

个人笔记

oplog 窗口问题是一个众所周知的问题。虽然使用 MongoDB 很容易设置副本集和分片集群,但需要相当多的知识和经验才能正确维护它们。不要在不了解基础知识的情况下运行具有复杂设置的数据库这样重要的东西 - 万一发生了坏事(tm),很可能会导致 FUBAR 情况。

【讨论】:

  • 感谢Mahlberg,我会试试你的方法。
  • 你拯救了我的一天!我将我的 oplog 大小设置为 1MB,它只能保持 3 小时的操作持续时间。现在,我需要将其设置为 30MB。 (我的节点在同一个数据中心,所以延迟不是大问题:))
  • 根据docs.mongodb.com/manual/tutorial/change-oplog-size的当前(2018年7月)版本,您似乎不需要关闭节点来调整oplog的大小。
  • @Maksym 对于 OP 的版本不是这样:docs.mongodb.com/v3.0/tutorial/change-oplog-size
  • @Markus W Mahlberg 当然。我应该说'无需关闭新版本 MongoDB 中的节点'。此页面是搜索特定同步问题时的第一次点击,不妨对 2019 年访问的人有所帮助。
【解决方案2】:

另一个选项(假设主节点有健康的数据)是简单地删除辅助节点的 mongo 数据文件夹中的数据并重新启动。这将导致它同步备份到主数据库,就像您刚刚将它添加到副本集一样。

【讨论】:

  • 如果数据比较多,也可能需要2-3年才能从primary同步到secondary。
【解决方案3】:

向副本集添加第四个新节点。同步后,重置陈旧的辅助节点。

【讨论】: