config server全部宕机后mongos进入元数据只读状态而非报错,因其设计为“静默降级”:无法刷新元数据缓存时拒绝分片管理命令,但依赖本地缓存(默认30秒ttl)继续转发普通读写请求,仅当缓存过期且config server未恢复时才停止元数据相关操作。

Config Server全部宕机后mongos为什么只读却不报错
因为mongos的设计逻辑是“元数据不可写即静默拦截”,不是连接失败就抛异常。只要config server副本集无法达成多数派(比如3节点全不可达),mongos立刻进入metadata read-only状态,但普通读写请求仍照常转发——它用的是本地缓存的路由表,只要没过期(默认30秒),业务完全无感。
常见错误现象包括:sh.moveChunk卡住几秒后报config server is unreachable,sh.status()能返回旧拓扑但sh.splitAt()直接失败,日志里反复出现Failed to refresh config database: NotMaster。
关键点在于:mongos不重试、不降级、不 fallback,它只等心跳恢复或人工干预。而默认心跳间隔是configServerHeartbeatFrequencyMS(10秒),连续失败后才触发只读,所以你看到“刚宕机就只读”其实是心跳已超时。
只剩一个Config Server存活时怎么强制让它当PRIMARY
不能等选举,必须人工重配副本集配置。因为少于多数派(3节点只剩1个),MongoDB拒绝自动触发选举,rs.status()会显示所有成员状态为SECONDARY或STARTUP2,db.runCommand({ismaster: 1})返回"ismaster" : false。
操作步骤:
- 连上那个存活节点:
mongo --host 10.0.1.100:27019 - 执行
cfg = rs.conf(),把其他两个已确认宕机节点的votes和priority全设为0(例如cfg.members[1].votes = 0; cfg.members[1].priority = 0) - 运行
rs.reconfig(cfg, {force: true})——{force: true}是必须的,否则会因“非 majority 状态”被拒绝 - 等几秒后查
rs.status().myState,返回1表示已升为PRIMARY;再看rs.status().members[n].stateStr应为PRIMARY
此时config库立刻可写,mongos会在30秒内自动刷新缓存——不用重启mongos,也不用手动触发reload。
替换宕机Config Server节点时最容易踩的坑
最危险的操作是直接rs.add()一个复用旧数据目录的节点。WiredTiger会因元数据版本冲突报Invalid argument: unable to read config.system.sessions,新节点卡在RECOVERING状态,副本集永远无法稳定。
正确做法必须满足三个硬性条件:
- 被替换节点的
dbpath目录必须彻底清空(不能rm -rf完还留着journal或 WiredTiger.turtle) - 启动参数必须和现存节点完全一致:
--replSet名、端口、--bind_ip、--configsvr标志缺一不可 - 加入副本集前,先用
mongorestore --drop恢复最近一次完整config库备份(含config.databases、config.collections、config.chunks)
替换后观察rs.status():新节点状态应逐步变为SECONDARY,且optimeDate追平PRIMARY。如果一直停在STARTUP2,大概率是数据目录没清干净或网络不通。
恢复后sh.status()还是旧拓扑?别急着重启mongos
mongos不会主动拉取新元数据,它依赖后台心跳检测+版本号变更来触发全量reload。即使config副本集已健康,sh.status()仍可能显示旧chunk分布,sh.moveChunk继续报错。
先确认两件事:
- mongos日志里是否出现
Successfully refreshed config database(不是Failed) - 连接mongos执行
db.adminCommand({getCmdLineOpts: 1}),核对parsed.configdb值是否包含当前存活的config server地址(比如configRepl/10.0.1.10:27019,10.0.1.11:27019,10.0.1.13:27019)
如果日志没刷新成功,等满2个心跳周期(默认20秒);如果configdb地址漏了新节点,必须重启mongos并带上正确的--configdb参数——这个细节90%的人会忽略,导致集群“看起来好了,实际路由表还是错的”。











