在 MongoDB 副本集里,从节点的数据一致性靠持续读取主节点的 oplog 来维持。这个 oplog 不是无限大的日志文件,而是一个固定在 local 库中的 capped collection。新写入会被追加,旧记录则按大小或条数被清理。如果从节点因为网络抖动、备份任务、慢磁盘或大批量写入产生延迟,它就会暂时落后于主节点。落后本身并不可怕,可怕的是主节点写入压力持续存在,oplog 被写完一圈后,从节点还没读到的操作已经被覆盖。此时从节点会向同步源请求一个已经消失的位点,610 错误就会出现在日志中,同步线程随之失败,节点可能进入 RECOVERING 状态,甚至被踢出健康节点列表。

一、610错误码背后的同步机制
要理解 610,首先要区分全量同步和增量同步。副本集新建一个从节点时,会先从主节点拉取一份数据快照做 initial sync。这个过程通常很重,但它是从零开始建立数据基础。之后进入增量同步阶段,从节点会通过 find 命令持续读取主节点 local.oplog.rs 集合,并按照 ts 字段的时间戳顺序回放操作。回放过程不是逐条复制 SQL 或命令,而是复用底层 oplog entry。
当从节点停止回放一段时间后再启动,它会根据自己已经执行的最后一个时间戳去同步源查找下一条。MongoDB 的 oplog 是有界的,写入操作用掉的空间会被回收。回收的依据就是 capped collection 的固定容量。如果这个容量只有几 GB,而业务在一小时内写入了同样量级的日志,那么一小时后从节点再回来,它要读的位点早就没有了。日志里常见的是 code 610,并伴随 cannot find the session 或 oplog start missing 之类的描述。其实本质都是同步游标已经失效。
有些同学会把 610 当成单纯的网络问题去重连,但重连无法解决问题,因为从节点的目标位点不会自己前移。只要主节点没有保留那个时间点的操作,从节点就没有任何办法只靠常规增量追平。必须通过扩容 oplog 或重新初始化来恢复。
二、快速确认 oplog 是否真的太小
排查时先看主节点的 oplog 基础信息。登录任意健康节点,执行 rs.printReplicationInfo(),输出里会有 configured oplog size、log length start to end 和 oplog first event time、last event time。其中时间跨度就是当前 oplog 能覆盖的窗口。比如输出显示 first event 是 09:00,last event 是 13:00,那么窗口就是 4 小时。也就是说从节点最多允许落后 4 小时,超过这个时间就会进入无法增量同步的状态。
rs.printReplicationInfo()
另一个更细的命令是 db.getReplicationInfo(),它会同时给出 allocated oplog size 和 timeDiff。注意不同版本输出字段不完全一样,但核心是时间窗口。拿到时间窗口后,再去统计当前写入速率。可以用 mongostat 观察 insert、update、delete 的 ops 数量,或者用 serverStatus 里的 opcounters 差值来估算。把 oplog 大小除以每秒写入字节数,就能得到理论可覆盖秒数。
db.getReplicationInfo()
如果发现 timeDiff 只有 30 分钟,而备份任务或批量导入经常让从节点滞后 1 小时,那问题就很明确:oplog 必须扩大。反之,如果 timeDiff 有好几天,但日志仍报 610,那就要检查同步源是否发生变化、是否存在多轮次写入导致 oplog 被手工截断,或者从节点回放速度慢于写入速度,已经累计到不可逆状态。后者的本质仍然是 oplog 不够覆盖从节点的追速时间。
三、处理方案:在线扩容、重建从节点和配置优化
对于 MongoDB 4.4 及以上版本,最优先尝试在线扩容。命令 replSetResizeOplog 允许用户在不需要重新同步成员的情况下调整 oplog 最小值。参数 size 的单位是 MB,例如调整到 20GB 就写 20480。可以在主节点上执行,系统会尝试把配置应用到各成员。执行前建议确认每个成员都有足够的未分配磁盘空间。
db.adminCommand({
replSetResizeOplog: 1,
size: 20480
})
如果版本较旧,或者节点已经无法追平,在线扩容可能已经来不及了,因为从节点当前需要的位点已经丢失。此时要把故障节点按从零重建。重建前先确认主节点 oplog 窗口在重建期间不会被写穿,否则同步到一半又会遇到同样的错误。可以选择业务低峰期,或者先临时把主节点 oplog 调大。重建从节点的常规步骤是停掉从节点、清空 dbPath,重新启动。它会自动进入 initial sync,不必手工导入数据。需要注意 dbPath 目录下如果有备份文件或其他数据,清空前要确认没有额外用途。
如果磁盘空间不富裕,但又不能接受频繁重建,也可以从业务侧降低单次批量任务写入粒度,或者错开大批量任务的时间。数据库配置方面,除了扩大 oplog 外,还要检查 writeConcern 和从节点读负载。某些场景下,从节点同时承担大量慢查询,导致回放跟不上,此时给从节点分配独立机器或限制慢查询会更有用。
四、用监控把故障消灭在延迟阶段
610 错误本质上是延迟累积到不可逆的结果,因此最有价值的监控指标不是错误日志本身,而是 oplog 时间窗口和从节点延迟。可以用 rs.printSlaveReplicationInfo() 或 rs.printSecondaryReplicationInfo() 查看每个从节点落后主节点多少秒。不同大版本命令略有差异,但输出中都会有 lag 或 last heartbeat 相关字段。把 lag 和主节点的 oplog timeDiff 放在同一张图里,当 lag 超过 timeDiff 的 50% 时就要告警。
rs.printSecondaryReplicationInfo()
在 Prometheus 体系中,可以采集 db.serverStatus().metrics.repl 和 db.serverStatus().opcounters,用速率函数算出 oplog write bytes per second,再结合 oplog size 计算窗口秒数。窗口秒数低于 12 小时就可以触发告警。也可以定期执行 mongostat 观察 inserted、updated、deleted 三个指标是否突增。如果业务上每天有固定大批量写入,提前把窗口调到能覆盖 2 到 3 倍批量时长,是成本最低的预防手段。
最后有一个容易忽略的点:扩容 oplog 后,旧节点不会自动增加,除非它自己是主节点或执行过 replSetResizeOplog。如果副本集中混合了不同版本,集群可能只在新版本成员上生效。建议在维护后逐台确认,确保所有成员都拥有近似一致的 oplog 容量,否则后续主节点切换时,新主仍可能暴露同样的故障。
MongoDB oplog副本集同步oplog大小修改时间:2026-09-20 09:45:44