必须逐级升级,因mongodb大版本间存储格式、复制协议、oplog结构不兼容;如4.2 oplog无法被5.0解析,6.0 clustertime v2被5.0拒绝,7.0 strictvalidation导致旧文档校验失败,官方强制要求4.2→4.4→5.0→6.0→7.0路径并设fcv后稳运24小时。

MongoDB 副本集跨版本滚动升级不能跳步,必须逐级升级、逐节点替换,否则 mongod 启动直接失败,或启动后拒绝加入副本集,报错 NodeNotFound、IncompatibleServerVersion 或静默丢弃心跳。
为什么必须逐级升级而不是直接换二进制?
MongoDB 的存储格式、复制协议、oplog 结构在大版本间有不兼容变更。例如 4.2 的 oplog 条目无法被 5.0+ 的节点正确解析;6.0 引入的 clusterTime v2 格式会被 5.0 mongod 拒绝同步;7.0 默认启用的 strictValidation 模式会让旧节点写入的文档校验失败。
官方明确禁止跨大版本(如 4.2 → 6.0)直接升级,强制要求路径为:4.2 → 4.4 → 5.0 → 6.0 → 7.0。每个中间版本都需完成 setFeatureCompatibilityVersion 调整并稳定运行至少 24 小时。
常见错误现象:
- 新节点启动后日志反复打印
Failed to load replica set config,且rs.status()中状态为STARTUP2长时间不推进 -
rs.add("new_node:27017")返回NotMaster,实际是因 fCV 不匹配被 PRIMARY 拒绝握手 - 升级后 PRIMARY 切换异常,
SECONDARY节点卡在RECOVERING,oplog同步停滞
滚动升级的正确顺序和每步关键动作
核心原则:永远先降级 PRIMARY,再升级;绝不 kill -9 或 systemctl stop 正在服务的 PRIMARY;所有节点升级前必须确认已同步至最新 oplog。
实操步骤(以三节点副本集 rs0 升级 4.4 → 5.0 为例):
- 执行
rs.stepDown(30),等待当前 PRIMARY 主动退位,确保rs.status().myState === 2(即已变 SECONDARY) - 用
rs.status()确认新 PRIMARY 已选出且状态稳定(stateStr: "PRIMARY"),且所有节点optimeDate时间差 - 停止待升级节点的
mongod进程(建议用mongod --shutdown或systemctl stop mongod) - 替换二进制文件(如
/usr/bin/mongod),**不要覆盖配置文件**;检查mongod.conf中storage.engine是否仍为wiredTiger(7.0 开始不支持 mmapv1) - 启动新版本
mongod,观察日志直到出现Waiting for connections且无 ERROR 级别报错 - 登录该节点执行
rs.status(),确认其状态变为SECONDARY并开始同步(syncSourceHost非空,optimeDate持续更新) - 重复以上流程,升级第二个 SECONDARY;最后对当前 PRIMARY 执行
rs.stepDown()再升级
featureCompatibilityVersion(fCV)必须手动设置且时机敏感
fCV 不是自动随版本升级而变更的,它控制的是“是否启用新版本功能”,必须在所有节点完成二进制升级、全部重新加入副本集且稳定同步后,才由 PRIMARY 统一设置。提前设或漏设都会导致数据损坏或复制中断。
操作要点:
- 升级完全部节点并确认
rs.status()全绿后,连上 PRIMARY 执行:db.adminCommand({setFeatureCompatibilityVersion: "5.0"}) - 立即验证:
db.adminCommand({getParameter: 1, featureCompatibilityVersion: 1})返回值应为"5.0" - 如果返回
"4.4",说明命令未生效——常见原因是连接了非 PRIMARY 节点,或副本集尚未完成初始化(rs.initiate()后需等 30 秒) - 设置 fCV 后,**不可降级回低版本**;若误设,只能通过完整备份恢复,无法 rollback
升级后必须验证的三项硬指标
很多团队只看 rs.status() 是 green 就认为成功,但实际可能已埋下隐患。以下三项必须人工确认:
-
oplog 可读性:在每个节点执行
db.getSiblingDB("local").oplog.rs.find().sort({$natural: -1}).limit(1).pretty(),确保能正常返回最新条目,且ts字段为Timestamp类型(不是字符串) -
写入一致性:向 PRIMARY 插入一条带唯一索引的文档(如
{_id: ObjectId(), ts: new Date()}),然后立刻在所有 SECONDARY 上查,确认find().count()= 1 且ts值一致 -
驱动兼容性:用生产环境实际使用的 driver(如
pymongo 4.7+、mongo-go-driver v1.13+)连接新集群,执行一次带事务的写操作,确认不报OperationNotSupportedInTransaction或InvalidNamespace
最容易被忽略的是 fCV 设置后的静默降级风险:一旦设为 "7.0",即使你只是临时回滚 mongod 二进制到 6.0,节点也无法再加入副本集——因为 6.0 完全不认识 7.0 的 fCV 元数据格式。升级不是换包那么简单,它是数据库协议层的一次重锚定。











