movechunk失败后数据在原分片和目标分片同时存在;已复制到目标分片的文档不会自动删除,源分片文档仍保留,导致临时重复。

MoveChunk失败后数据还在原分片还是已部分写入目标分片?
MoveChunk 操作不是原子性迁移,而是分阶段推进:先复制数据、再切换读写、最后清理源数据。一旦失败,moveChunk 命令会中断,但**已复制到目标分片的文档不会自动删除**,而源分片上对应文档仍保留——这导致临时性重复,而非直接丢失。
关键判断点是查看 sh.status() 中该 chunk 的状态,以及检查两个分片上 config.changelog 的最后几条记录:
-
from和to字段明确标识迁移方向 -
state为started或committed表示已推进到复制/提交阶段 -
detail字段可能包含aborted或具体错误(如LockTimeout、NotMaster)
如何确认哪些文档实际被“半途迁移”了?
不能靠肉眼比对集合文档数——因为 chunk 边界和索引键分布会影响实际迁移范围。正确做法是用 sh.status() 定位问题 chunk 的 min 和 max 键值,再分别在源分片和目标分片上执行范围查询:
db.collection.find({ _id: { $gte: ObjectId("..."), $lt: ObjectId("...") } }).count()
注意:_id 只在默认分片键时有效;若用自定义分片键(如 { region: 1, ts: 1 }),需用对应字段构造查询条件。同时检查 config.databases 和 config.collections 确认分片是否仍处于 enabled: true 状态,避免误判集合未分片。
回滚 MoveChunk 失败的唯一可靠方式是手动清理 + 元数据修复
MongoDB 不提供自动回滚命令。所谓“回滚”其实是人工干预三件事:
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 停写应用,防止新数据写入冲突区域
- 在目标分片上,用
remove删除已复制但未提交的文档(依据config.changelog中的from/to和时间戳筛选) - 在
config数据库中,用sh.updateZoneKeyRange或直接更新config.chunks,把 chunk 的shard字段改回原分片名,并递增lastmod时间戳(格式为Timestamp(123, 456)) - 运行
sh.stopBalancer()后执行sh.startBalancer()触发元数据同步,观察sh.isBalancerRunning()是否恢复
切勿直接删 config.chunks 文档——缺失 chunk 记录会导致后续所有路由请求失败。
为什么 mongodump/mongorestore 不能替代 MoveChunk 回滚?
因为 mongodump 只导出当前可见数据快照,无法区分“本该在源分片但被复制走的文档”和“本就属于目标分片的其他 chunk”。更危险的是:如果在迁移失败后立即 dump,可能漏掉源分片上尚未被 balancer 清理的旧数据,或重复 dump 目标分片上未提交的副本。
真正能用的备份手段只有两类:
- 迁移前已存在的
fsync + lock全量备份(要求storage.journal: true) - 基于 oplog 的增量恢复(需提前开启
oplogSize并持续拉取)
没有这两者,就只能接受现状并从最近一致快照重建——这也是为什么 MoveChunk 前必须验证 config.settings.balancer.stopped 为 false 且无正在运行的迁移任务。










