Config Server全部宕机后,mongos立即进入元数据只读状态,普通读写不受影响;需恢复config副本集多数派并触发心跳刷新,而非仅重启单节点。

Config Server 全部宕机后,mongos 会立即进入元数据只读状态,但普通读写不受影响——恢复关键不是“修好旧节点”,而是让 config 副本集重新达到多数派可用,并确保 mongos 能感知到。
为什么不能只重启一个 Config Server?
CSRS 模式下,config server 是三节点副本集。如果只剩一个节点在线(或全部宕机),mongos 会因无法获取多数派确认而拒绝所有元数据写操作,比如 sh.moveChunk、sh.enableSharding。它不会等、不重试、不降级,只静默拦截。
此时即使你手动启动一个 config server 进程,若副本集状态未恢复(比如 rs.status() 显示 STARTUP2 或 RECOVERING),mongos 仍认为 config 不可用。
- mongos 默认每
configServerHeartbeatFrequencyMS(默认 10 秒)向 config server 发心跳,连续失败后才触发只读降级 - 恢复后 mongos 不会自动刷新缓存,需等待下一次心跳成功 + 元数据版本号变更才会全量 reload
- 直接替换成员前,必须确认原节点数据目录已损坏或不可信;否则新成员同步可能拉入脏数据
如何用新节点替换宕机的 Config Server 成员?
目标是让 config 副本集重新拥有 ≥2 个可投票成员(即多数派),从而允许写入和 mongos 正常刷新。
假设 config 副本集原为 configRepl/10.0.1.10:27019,10.0.1.11:27019,10.0.1.12:27019,其中 10.0.1.12 宕机且无法恢复:
- 在新机器上部署干净的
mongod,配置replSet: configRepl、clusterRole: configsvr,绑定新 IP(如10.0.1.13) - 启动新节点,但先不要加入副本集;用
mongorestore --drop恢复最近一次完整的config库备份(必须含config.databases、config.collections、config.chunks等) - 连接存活的 config 主节点(如
10.0.1.10),执行rs.remove("10.0.1.12:27019")移除旧成员 - 再执行
rs.add({"_id": 2, "host": "10.0.1.13:27019", "priority": 0, "votes": 1})加入新节点 - 观察
rs.status():新节点状态应变为SECONDARY,且optimeDate逐步追平主节点
替换后 mongos 还不刷新路由怎么办?
常见现象:config 副本集已健康(rs.status().members[n].stateStr === "PRIMARY"),但 sh.status() 显示旧拓扑,sh.moveChunk 仍报 "config server is unreachable"。
这不是缓存没更新,而是 mongos 尚未完成一次成功的元数据刷新周期:
- 检查 mongos 日志是否有
"refreshed metadata from config server"类似条目;没有则说明心跳未成功 - 确认 mongos 配置中
configdb指向的是副本集名称(如configRepl/10.0.1.10:27019,10.0.1.11:27019,10.0.1.13:27019),而非单点地址 - 强制刷新:对每个 mongos 执行
db.adminCommand({flushRouterConfig: 1}),它会立刻丢弃本地缓存并从 config server 重拉 - 若仍失败,临时增加
refreshLogicalSessionCacheRefreshPeriodMS(默认 30000)降低刷新频率压力,避免雪崩
误删元数据后还能靠替换成员恢复吗?
不能。替换成员只是解决「节点不可达」问题,但无法修复「元数据内容错误或缺失」。
Config Server 的 oplog 不记录元数据变更,所以即使新节点从主节点同步,也只会复制当前错误的 config 库状态。一旦确认发生误删(如 db.config.databases.remove({})),唯一安全路径是:
- 停掉所有 mongos 和 balancer(
sh.setBalancerState(false)) - 将 config 副本集全体降为单节点模式(关闭
replSet配置,用--replSet null启动) - 用
mongorestore --drop恢复早于误删时间点的完整config库备份 - 再重新初始化副本集(
rs.initiate())、逐个加回成员
这个过程里最容易被跳过的一步是:恢复前没停 balancer,导致迁移任务在元数据错位时继续运行,最终引发 chunk 数据与路由表严重脱节——查不到、重复返回、甚至丢失文档。











