MongoDB 复制延迟故障排除:原因与解决方案

了解如何诊断和解决 MongoDB 副本集中的复制延迟问题。本指南涵盖常见原因,包括高写入负载、硬件瓶颈和网络问题。探索使用 `rs.printReplicationInfo()` 的实用监控技术以及维护数据同步的实用解决方案,确保所有数据库节点的高可用性和读取一致性。

MongoDB 复制延迟故障排除:原因与解决方案

MongoDB 复制延迟通常始于一个小的操作烦恼。图表开始攀升。一个从节点落后 15 秒,然后 2 分钟。有人询问读取是否过时。其他人建议重新启动节点。在你这样做之前,先放慢速度,找出复制中哪个部分正在落后。

MongoDB 从节点从主节点的 oplog 复制操作并在本地应用它们。复制延迟意味着从节点应用操作的时间不如主节点那么近。这可能会影响从节点读取、从从节点进行的备份、分析作业和故障转移。它还可能隐藏一个更大的风险:如果从节点落后于 oplog 窗口,它可能根本无法从 oplog 中追赶上来。

最快的故障排除路径是回答三个问题:

  • 每个从节点都在延迟,还是只有一个?
  • 延迟是暂时的、稳定的还是不断增长的?
  • 从节点是否仍在 oplog 窗口内?

这些答案决定了你接下来要做什么。

无需猜测即可测量延迟

mongosh 开始:

rs.status()

找到主节点并将其 optimeDate 与每个从节点的 optimeDate 进行比较。还要查找不健康的成员、心跳消息以及卡在 RECOVERINGSTARTUP2 等状态的成员。

要获得更友好的摘要,请运行:

rs.printSecondaryReplicationInfo()

一些较旧的资料使用 rs.printSlaveReplicationInfo()。如果你维护较旧的系统,你可能仍然会看到那个辅助函数。现代措辞是“secondary”。

然后检查 oplog 窗口:

rs.printReplicationInfo()

oplog 窗口是当前在 oplog 中保留的历史记录量。如果你的从节点落后 40 分钟,而 oplog 窗口是几天,你就有空间进行故障排除。如果你的从节点落后 40 分钟,而 oplog 窗口在高峰流量期间是 1 小时,你就接近需要重建的情况了。

不要仅仅依赖来自单个工具的 SecondsBehind 风格的值。时钟偏差、延迟成员和短暂的突发可能使一个数字产生误导。将状态输出与写入量、磁盘延迟、CPU 和网络吞吐量的监控图表进行比较。

如果所有从节点都在延迟

当每个从节点大致在同一时间落后时,原因通常在任何单个从节点上游。首先查看主节点的写入工作负载。

常见触发因素包括:

  • 批量导入或回填。
  • 大型 updateManydeleteMany 操作。
  • 积压一段时间后的 TTL 清理。
  • 更改写入量的应用程序部署。
  • 索引构建或模式维护。
  • 创建许多 oplog 条目的小型写入突然增加。

询问在延迟开始时发生了什么变化。一个在夜间作业开始时精确开始的峰值很少是 MongoDB 的谜团。

在主节点上,检查活动操作:

db.currentOp({ active: true })

如果你发现一个批处理作业,考虑限制它而不是让它以最大速度完成。例如,在 _id 范围内处理文档,在批次之间休眠,并观察延迟。这对于清理作业特别有用,在 30 分钟内完成不如保持副本集健康重要。

如果持续的写入量只是高于副本集可以处理的水平,你需要进行容量或架构更改。更好的磁盘、更多的 CPU、不同的实例类、写入路径优化或分片可能是正确的答案。更改读取偏好不会修复一个产生的工作量超过集合可以应用的主节点。

如果只有一个从节点在延迟

一个延迟的从节点通常指向本地问题。登录到该主机并检查基本情况:

iostat -xz 1
vmstat 1
top

在 MongoDB 内部,使用:

mongostat --host secondary.example.com:27017
mongotop --host secondary.example.com:27017

磁盘是常见的罪魁祸首。使用比主节点更慢的存储的从节点在正常流量期间可能没问题,但在突发期间会落后。云卷也可能达到吞吐量或 IOPS 上限。寻找高利用率、高等待时间和排队。

当工作负载包括许多更新、压缩、加密或同一成员上的大量查询流量时,CPU 可能很重要。当从节点无法在应用写入的同时将热数据和索引保留在缓存中时,内存压力很重要。

还要检查主机上运行的其他内容。备份、防病毒扫描、文件系统快照、日志压缩和报告查询都可能与复制竞争。如果延迟节点也是每个人运行临时分析的“安全场所”,你可能已经找到了问题。

从节点上的读取可能造成延迟

从节点读取不是免费的。它们使用与复制相同的缓存、CPU 和磁盘。一个扫描大型集合的单个聚合就足以使从节点在繁忙期间落后。

寻找长时间运行的读取:

db.currentOp({ active: true })

如果应用程序将读取发送到从节点,请检查读取偏好。secondary 可能强制读取到延迟成员。secondaryPreferred 仍然可能返回过时数据。对于必须读取自己写入的用户流程,请使用主节点。对于最终一致性读取,设置 maxStalenessSeconds,以便驱动程序避免落后太多的从节点。

对于报告工作负载,考虑使用隐藏的从节点或单独的分析管道。隐藏成员仍然可以复制,但驱动程序不会为正常读取选择它们。只要正确调整它们的大小,这使它们成为备份或受控报告作业的更好场所。

Oplog 大小是恢复余量,而不是速度修复

太小的 oplog 通常不会单独导致延迟。它使延迟变得危险。如果从节点落后并且所需的 oplog 条目被覆盖,它就无法正常追赶。

你的 oplog 窗口应该比你的实际故障和维护场景更长。如果从节点可能在修补期间离线 6 小时,那么 4 小时的 oplog 窗口是不够的。如果季度导入在几小时内消耗完 oplog,请为该工作负载调整大小或更改导入的运行方式。

在支持的版本上,使用 replSetResizeOplog 在每个需要更大 oplog 的成员上调整大小:

use admin
db.adminCommand({ replSetResizeOplog: 1, size: 20480 })

该示例请求大约 20 GB。在托管平台上,使用托管配置方法。避免删除并重新创建 oplog 的旧建议,除非你遵循经过仔细测试的恢复程序。

增加 oplog 后,继续对底层延迟进行故障排除。更大的 oplog 给你更多时间;它不会消除磁盘饱和、网络限制或过多的写入突发。

实际有用的网络检查

当延迟影响远程从节点、一个可用区或一个数据中心路径时,网络问题更有可能。从简单开始:

ping primary.example.com
traceroute primary.example.com

然后查看延迟之外。复制需要可靠的吞吐量。即使 ping 看起来可以接受,数据包丢失、防火墙检查、VPN 限制、跨区域带宽上限或过载的网络接口也可能造成延迟。

如果只有跨区域成员延迟,请将其与相同写入负载下的本地从节点进行比较。你可能需要不同的拓扑结构、更大的链路或更清晰的期望,即远程成员用于灾难恢复而不是新鲜读取。

数据和索引漂移

副本集成员应具有相同的索引。如果它们没有,oplog 应用可能会变慢或失败。这通常来自手动更改、失败的维护或从不一致来源恢复的成员。

比较热门集合上的索引:

db.orders.getIndexes()

在主节点和延迟的从节点上运行它。如果定义不同,请有意修复漂移。重建大型索引可能会增加更多负载,因此请仔细安排它,或者如果差异广泛,则从干净的源重建成员。

数据分歧更严重。如果复制错误显示丢失的记录或重复的键,延迟不再是唯一的问题。你需要检查错误,比较数据,并决定表级修复、重新同步或完全重建哪个是最安全的路径。

谨慎使用重启和初始同步

如果进程卡在临时问题后面,重新启动延迟的从节点有时会有所帮助。这不是一个通用的修复。如果成员接近 oplog 窗口的边缘,重启可能会花费足够的时间将其推入不可恢复的状态。

在重启之前,检查:

  • 当前延迟。
  • 当前 oplog 窗口。
  • 成员是否正在同步。
  • 是否存在其他健康的从节点。
  • 副本集是否可以容忍该成员宕机。

当从节点无法追赶或其数据不可信时,初始同步是干净的答案。它也很重。它复制数据、构建索引并消耗来自另一个成员的资源。一次重建一个成员,并确保你的投票配置在节点重建期间仍然支持安全选举。

何时不应该急于修复

在受控工作期间,一些延迟是预期的。如果你正在运行计划中的回填、恢复从节点或导入历史数据,有用的问题是,从节点是否以可接受的速度追赶。一个上升 20 分钟然后稳步下降的延迟图可能不需要干预。一个每天上升且从未回到基线的延迟图则需要。

这种区别很重要,因为一些修复具有破坏性。杀死批处理作业可能会使应用程序数据半更新。重新启动从节点可能会损失缓存热度并使追赶变慢。重建成员可能会消耗比简单地让它应用积压更多的网络和磁盘。

对于计划中的作业,在作业开始前设置一个延迟预算。例如,你可能决定维护回填可以在报告从节点上产生最多 10 分钟的延迟,但不能在故障转移候选者上。在作业运行时观察延迟、oplog 窗口和写入速率。如果作业接近预算,暂停它或减少批处理大小。

将面向用户的副本与维护副本分开也有帮助。用于应用程序读取的从节点应该比用于备份的隐藏成员具有更严格的延迟容忍度。如果每个从节点都有不同的工作,警报阈值应该反映这些工作,而不是为整个集合使用一个数字。

事件期间要记录的内容

如果你保存了正确的证据,复制事件在事后更容易理解。在更改配置之前,捕获:

rs.status()
rs.conf()
rs.printReplicationInfo()
rs.printSecondaryReplicationInfo()

还要保存来自主节点和延迟从节点的主机级指标:磁盘延迟、CPU、内存和网络吞吐量。如果批处理作业或部署正在运行,记录其开始时间和命令或发布版本。

这不是为了文书工作本身。没有时间线,下一个事件从零开始。有了时间线,你可能会注意到延迟总是跟随特定的导出、备份或清理任务。这将一个模糊的数据库问题变成了一个可调度的容量问题。

实用的修复地图

使用症状来选择下一步行动:

症状 可能区域 下一步行动
所有从节点在批处理作业期间延迟 写入突发 限制或拆分作业
一个从节点总是延迟 本地资源问题 检查磁盘、CPU、内存和本地读取
仅在远程成员上延迟增长 网络/拓扑 检查吞吐量、数据包丢失和跨区域设计
延迟接近 oplog 窗口 恢复风险 增加 oplog 并减少延迟源
从节点提供过时读取 读取偏好 使用主节点进行新鲜读取或设置 maxStalenessSeconds
成员在停机后无法追赶 缺少 oplog 历史 从备份或初始同步重建

良好的 MongoDB 复制故障排除主要是纪律严明的观察。找出是主节点产生过多工作、从节点应用过慢还是它们之间的链接受到限制。然后更改实际限制复制的东西,而不是应用通用的重启、重新同步或配置调整。