MongoDB复制集里启用延迟节点本是为了防范误删库、逻辑错误等人为故障,但运维过程中偶尔会碰到错误提示中包含故障码1690的情况。该状态表示延迟节点在尝试从同步源获取并重放oplog时,因为本地数据落后过多且受限于延迟配置,无法在允许的边界内完成追平,同步线程被强制中断。如果不加处理,这个节点会长期停留在恢复失败状态,一旦主节点发生数据损坏,延迟节点的保护能力也就失效了。

故障码1690的底层触发机制
在MongoDB的复制集协议中,普通从节点会尽可能快地应用主节点产生的oplog,而延迟节点则故意等待配置中指定的slaveDelay秒数后再重放。这种设计依赖一个前提:同步源上保留的oplog必须覆盖延迟节点所需要的全部操作区间。当主节点写入量突增、或者延迟节点因网络抖动停机过久,重新上线时它需要的oplog起点可能已经被同步源滚动清理掉,此时就会报出1690类的恢复异常。
从代码层面看,MongoDB的同步逻辑会先计算本节点最新应用的optime,再向同步源请求后续记录。如果源端返回的通知表明所需起点小于其现存最早oplog,就会判定无法在延迟约束下安全追平。很多新手会误以为调大oplogSizeMB就能解决,其实若延迟窗口本身大于oplog留存时长,扩容也只是延缓而非根除问题。理解这一点,才能制定正确的恢复方案。
另一个容易被忽略的点是,延迟节点在报错后并不会自动降级为普通节点,它依然坚守slaveDelay设置。这意味着任何“强制同步”操作如果不尊重该属性,都可能让节点瞬间追平主库,从而失去延迟备份的意义。因此恢复流程必须把“保留延迟”作为硬约束,而不是简单重新加回集群。
保留延迟属性的安全恢复步骤
面对1690,最稳妥的方式是对延迟节点执行重新同步(resync),但要在配置文件中维持原有的slaveDelay值。具体做法是先停止该节点进程,删除其数据目录下的全部文件,再以原配置重启。节点启动后会进入initial sync阶段,从同步源全量拷贝当前数据,完成后按照延迟设置继续滞后重放,既清除了旧oplog断点,又保住了容灾角色。
如果业务不允许长时间停节点,可以采用隐藏节点辅助法:在集群里临时加一个hidden节点专门做全量备份,再把备份物理拷贝到故障延迟节点机器上,修改本地配置指向该数据。这样延迟节点几乎无感知地完成数据置换。下面是一段典型的重新同步前清理脚本示例:
# 停止mongod服务 systemctl stop mongod # 备份原数据目录以防误操作 mv /var/lib/mongodb /var/lib/mongodb_bak # 重建空数据目录 mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb # 确保配置文件中的slaveDelay未被改动 grep slaveDelay /etc/mongod.conf
上述操作完成后,重启节点即可看到日志中重新出现initial sync字样。需要观察的是,全量同步期间该节点不提供读服务,也不计入投票多数,因此集群写入不受影响但容错票数暂时减少,应在低峰期执行。恢复后可用rs.status()确认其stateStr为SECONDARY且secondaryDelaySecs与预期一致。
预防1690再次发生的关键配置
根治1690不能只靠事后恢复,还要在规划阶段平衡延迟时长与oplog容量。经验公式是:oplog可留存时间应大于slaveDelay加上一次全量同步耗时再加安全余量。例如延迟设为一小时,全量同步最坏两小时,那oplog至少要保留四小时写入量。可通过估算业务峰值每秒写字节数来反推oplogSizeMB。
另外建议为延迟节点单独指定性能更好的同步源,避免它从另一个也带延迟的节点拉数据,形成链条过长而超时。在MongoDB 4.4之后支持configureDefaultReadConcern与流式oplog,可以适当缓解追赶压力。以下片段展示如何用命令查看当前oplog时间窗口:
// 连接到主节点执行
use local
// 计算最早与最新oplog时间差(秒)
var first = db.oplog.rs.find().sort({$natural:1}).limit(1).toArray()[0];
var last = db.oplog.rs.find().sort({$natural:-1}).limit(1).toArray()[0];
print((last.ts.getTime() - first.ts.getTime()) / 1000);
最后,监控上要把延迟节点的replication lag与oplog自由空间并列告警。一旦落后秒数逼近oplog覆盖边界,自动触发扩容或临时缩小延迟,都比等1690爆发再手工干预更可靠。把恢复流程写成运维手册,可大幅缩短故障处理时间。