replication lag不是故障,而是同步延迟;需对比primary与secondary的optimedate确认延迟值,若差值为0且numapplied增长慢,则apply线程堵塞,常见原因包括wiredtiger缓存不足、大文档更新、索引构建、磁盘io饱和或备份/慢查询抢占资源。

确认是否真滞后,别把replication lag当故障
SECONDARY节点显示“同步慢”,大概率只是replication lag,不是数据损坏或不同步。先别重启、别重同步,连到该节点执行:rs.status().members[n].optimeDate,再连PRIMARY查rs.status().members[0].optimeDate,两者时间差就是实际延迟。若差值
常见误判点:
- 用
rs.printSlaveReplicationInfo()(MongoDB 4.4+ 已弃用),结果可能缓存或不准 - 看到
stateStr: "SECONDARY"就认为健康,但health: 0或syncSourceHost为空说明已失联 - 日志里出现
"could not find base oplog entry"才是真断裂,不是“慢”,是“断”
查oplog窗口是否被覆盖,这是80%延迟的根因
执行db.getReplicationInfo(),重点看timeDiffHours——它代表当前oplog能覆盖的时间窗口。生产环境建议 ≥ 24 小时,写入密集场景建议 48–72 小时。如果这个值只有 2–3 小时,而你的写入峰值持续 5 小时以上,SECONDARY必然追不上,反复卡在“追赶模式”。
扩容oplog(4.4+)只需在PRIMARY上运行:
db.adminCommand({replSetResizeOplog: 1, size: 10240})
注意:
- 单位是 MB,不是 GB;
size必须为整数 - 命令执行时会短暂阻塞写入(毫秒级),但不中断服务
- 旧版本(≤ 4.2)无法在线调整,必须停机重建oplog,风险极高
- 扩容后立即验证:
db.getReplicationInfo()中maxSizeMB和timeDiffHours是否更新
检查同步源是否异常,链式复制常被忽略
运行rs.status(),查看目标SECONDARY的syncingTo字段。如果它不是指向PRIMARY,而是另一个SECONDARY(比如"syncingTo" : "node2:27017"),说明启用了链式复制(chained replication)。这本身合法,但会放大延迟——A→B→C 的链路,C 落后 A 的时间 = A→B + B→C。
禁用链式复制需修改副本集配置:
conf = rs.conf() conf.settings.chainingAllowed = false rs.reconfig(conf)
操作前确认:
- 所有节点版本 ≥ 4.0(否则
chainingAllowed不可用) -
rs.reconfig()必须在PRIMARY上执行,且version要自增 - 禁用后,所有SECONDARY将直接从PRIMARY拉取oplog,主节点网络/IO压力会上升
盯住apply阶段卡点,拉到了≠写进了
oplog拉取(fetch)通常是快的,真正慢在回放(apply)。查db.serverStatus().metrics.repl.apply里的queued和numApplied:如果queued持续 > 0 且numApplied增长缓慢,说明apply线程堵了。
典型堵点:
- WiredTiger cache不足:
db.serverStatus().wiredTiger.cache中maximum bytes configuredvsbytes currently in the cache,后者接近前者时apply变慢 - 大文档更新或索引构建中:
db.currentOp({ "secs_running": { "$gt": 30 } })查长时间运行的applyOps操作 - 磁盘IO饱和:
iostat -x 1看%util是否持续 >90%,尤其await高说明设备响应慢 - SECONDARY同时跑备份或慢查询:这类负载会抢占apply所需的CPU和IO资源
apply卡住时,单纯调大oplog或换同步源都没用——得先释放节点资源,或临时降低写入压力。











