分片集群无法直接降级为副本集,必须重建:需逐个导出各shard主节点数据,清空目标副本集的config元数据,再导入并修复唯一索引、写关注及读偏好配置。

分片集群直接降级不可行,必须重建
不能把正在运行的分片集群“切一刀”就变成副本集——mongos、config server 和 shard 的元数据结构、路由逻辑、集合分片状态全部耦合在一起,没有现成命令或配置能一键回退。强行停掉 mongos 并连接某个 shard 节点,你看到的只是碎片化数据(比如只有某几个 chunk),且 _id 可能重复、索引不全、事务/写关注失效。
导出时必须从每个 shard 单独 dump,不能只连 mongos
连 mongos 执行 mongodump 会触发路由查询,但结果不可靠:它可能漏掉未被路由到的 chunk(尤其当 chunk 迁移未完成或 balancer 关闭时),也可能因读取隔离级别问题拿到不一致快照。正确做法是逐个登录每个 shard 的主节点(不是 mongos),用 mongodump --host <shard-primary-host> --port <port> 导出完整库表。
- 确认每个
shard是独立副本集,且你连接的是当前 primary(查rs.status().members) - 加
--forceTableScan避免因缺失_id索引导致 dump 失败(某些旧分片表可能没建) - 别用
--archive合并多个 shard 数据——不同 shard 的同名集合实际是不同数据子集,合并会覆盖或冲突
导入前要清空目标副本集并禁用 balancer
在新建的单副本集上直接 mongorestore,如果之前有同名库表,残留的分片元数据(如 config.collections 中的 key 字段)会导致后续写入异常,甚至让 mongod 拒绝插入。必须先彻底清空:
- 停掉所有
mongos和config server,确保无外部访问 - 对目标副本集执行
db.getSiblingDB("config").collections.remove({})和db.getSiblingDB("config").chunks.remove({}) - 用
mongorestore --drop导入每个 shard 的 dump 目录(注意顺序:先导入admin和local外的业务库,最后补system.*表如必要)
重建后要重设唯一索引和读写关注
分片环境下,_id 索引默认是唯一但跨 shard 保证,而副本集里必须本地唯一;另外,原分片集群中依赖 majority 写关注的应用,在单副本集下会卡住(因为等不到多数节点确认)。这些不会自动修复:
- 检查每个集合是否已有
_id索引:db.collection.getIndexes(),若缺失或非唯一,手动重建:db.collection.createIndex({ _id: 1 }, { unique: true }) - 修改应用连接字符串,去掉
?w=majority,或改用w=1;否则写操作超时失败 - 如果用了
readPreference=nearest或readConcern=majority,在单节点下也需调整,否则读请求会 hang 住
最麻烦的其实是应用层隐式依赖:比如按 user_id 分片的集合,代码里写了“查 user_id=123 就一定在 shard0”,这种硬编码逻辑在副本集里完全失效,得通读业务代码扫一遍。










