上一篇讲了 oplog 是复制的载体,Secondary 靠拉取重放 oplog 追 Primary。但「追」这个动作不是实时的,Secondary 总会有一点点延迟。少量延迟正常,但持续的大延迟是问题——它会让读 Secondary 的应用拿到过时数据,更严重的是,failover 时延迟的数据可能丢失(回滚)。
理解复制延迟,才能回答几个运维问题:为什么读 Secondary 偶尔返回旧数据?为什么加节点时 Primary 会变慢?为什么 failover 后有些写入「消失」了?这些都和复制延迟相关。
先把机制边界说清楚
复制延迟的本质是 oplog 产生速度 vs Secondary 重放速度 的差:
- Primary 持续产生 oplog(每个写操作一条)。
- Secondary 持续拉取并重放 oplog。
当产生速度 > 重放速度,Secondary 就会越落越后,延迟增大。延迟用时间衡量:Secondary 当前应用的 oplog 时间戳,比 Primary 最新时间戳落后多少秒。
理想状态下,Secondary 重放速度略快于产生速度,延迟趋近于 0(通常亚秒级)。一旦重放跟不上,延迟就会累积。
延迟的四个根因
延迟很少只有一个原因,通常是几类因素叠加:
① 产生太快。批量写入是延迟最常见的诱因。一次性 insertMany 几百万条、跑数据导入、补数据脚本,会在短时间内产生海量 oplog,Secondary 重放速度跟不上这波洪峰。应对是错峰、分批、限速——把大批量写入拆成小批,控制产生速率。
② 重放太慢。Secondary 的硬件性能不足,重放 oplog 比 Primary 慢。常见于 Secondary 配置低于 Primary(为了省钱用低配从节点)。复制集里 Secondary 的配置不应该明显低于 Primary,否则它既追不上、failover 时也撑不住主的角色。
③ 网络瓶颈。跨机房、跨地域的复制,受网络带宽和延迟限制,oplog 传输本身就有延迟。跨城复制延迟几十毫秒很正常,跨洲可能上百毫秒。应对是压缩 oplog 传输、就近部署、必要时接受延迟。
④ 索引拖累。每个写操作在重放时要更新所有索引,索引越多重放越慢。一个有 10 个索引的集合,Secondary 重放单条 oplog 要做 10 次索引更新。建索引期间(Secondary 在建索引)延迟会更明显。控制索引数量是缓解之道。
延迟的危害
读 Secondary 拿到旧数据。应用用 readPreference: secondary 读时,拿到的数据最多落后延迟秒数。对一致性敏感的场景(下单后立即查订单),这会出问题。应对是给读设置 maxStalenessSeconds,限制可接受的延迟上限,超过就读 Primary。
failover 丢数据(回滚)。Primary 故障切换时,如果某些写入还没复制到多数 Secondary,这些写入在新 Primary 上不存在。MongoDB 会把这些「孤立的」写入写入回滚文件(rollback),数据实质上丢了。延迟越大,failover 时可能回滚的数据越多。用 w: "majority" 写能避免这个问题(下一篇展开)。
节点掉队触发全量初始化。延迟大到要拉的 oplog 已被覆盖,Secondary 必须全量重新初始化,进一步加重 Primary 负载。
监控与应对
监控复制延迟用 rs.printSecondaryReplicationInfo() 或 rs.status(),看每个 Secondary 的 replicationLag。健康的复制集延迟应该在秒级以内,持续超过 10 秒就要排查。
应对策略按根因:
- 产生太快 → 错峰分批限速,别在高峰跑批量。
- 重放太慢 → Secondary 配置别低于 Primary,检查 CPU/IO。
- 网络瓶颈 → 压缩、就近部署、评估是否需要跨地域。
- 索引拖累 → 控制索引数量,建索引错峰。
一个容易被忽略的点:读 Secondary 会放大延迟的感知。如果业务大量读 Secondary 而延迟又大,用户会明显感觉到「数据不一致」。对一致性敏感的读,宁可贵一点也读 Primary,不要为了分摊负载读延迟大的 Secondary。
判断框架
- 延迟本质 = 产生速度 - 重放速度,任一边失衡都会累积。
- 健康延迟秒级以内,持续 > 10s 要排查。
- 四类根因:产生太快、重放太慢、网络、索引。
- 读 Secondary 设
maxStalenessSeconds,一致性敏感读 Primary。 - failover 回滚的根因是延迟 + 非 majority 写,核心数据用
w: "majority"。 - Secondary 配置别低于 Primary,否则追不上也撑不住。
下一篇讲选举和 failover,看清 Primary 切换的判断逻辑。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MongoDB」,欢迎关注公众号 「十三Tech」。后续会按复制集、分片集群和架构选型这条线更新。

