必须用sh.moveChunk()的场景包括:批量写入后单chunk超2×chunkSize被Balancer跳过、需强制固定业务热点到指定分片、灰度扩容中新分片长期空载、哈希分片键下须改用底层moveChunk+bounds参数。

直接调用 sh.moveChunk() 是可行的,但绝大多数情况下不推荐——它绕过了负载均衡器的调度逻辑,容易引发数据倾斜、jumbo chunk 或迁移卡死。只有在明确知道目标 chunk 存在且未被标记为 jumbo、目标分片健康、且已关闭自动迁移时,才考虑手动触发。
什么时候必须用 sh.moveChunk() 而不是等 Balancer?
自动均衡器不会处理以下场景,此时需人工介入:
- 刚完成批量写入,某分片突然承载了远超平均值的数据(比如单个 chunk 实际大小 > 2×
chunkSize,被 Balancer 主动跳过) - 需要将某个业务热点(如
{tenant_id: "abc123"})强制固定到指定分片做本地性优化 - 分片集群正在做灰度扩容,新加入的分片长期空载,Balancer 还没开始迁移
- 哈希分片键下,
sh.moveChunk()本身不可用,必须改用底层moveChunk命令 +bounds参数——这点极易被忽略
sh.moveChunk() 的三个参数怎么填才不出错?
参数表面简单,但填错一个就会报错或静默失败:
-
namespace必须是完整命名空间,例如"mydb.users",不能漏掉数据库名,也不能写成mydb.users(缺引号)或"mydb.users."(尾部点号) -
query必须是分片键的**等值匹配**,且该值必须真实存在于某个 chunk 中。例如分片键是{region: 1, user_id: "hashed"},就不能传{region: "us-east"}——因为哈希分片键不支持范围/前缀匹配;必须传完整键值对,如{region: "us-east", user_id: NumberLong("123456789")} -
destination必须是sh.status()或db.runCommand({listShards: 1})返回的_id字段值,常见错误是填了主机名(如"shard01.example.com:27018")或别名(如"shard01"),正确值类似"shard0001"
执行前必须检查的五件事
跳过任一检查,都可能造成迁移中断、chunk 卡住或元数据不一致:
- 确认 Balancer 已停:运行
sh.getBalancerState(),返回true则先执行sh.stopBalancer();否则 Balancer 可能和你并发操作同一 chunk - 确认目标分片在线且有足够空间:用
db.runCommand({listShards: 1})查host和state,再连上目标分片查db.stats().dataSize是否余量充足 - 确认源 chunk 不是 jumbo:运行
db.adminCommand({splitVector: "mydb.users", keyPattern: {region: 1}, maxChunkSizeBytes: 67108864}),看返回中是否有"jumbo": true;若有,必须加forceJumbo: true参数调用原生命令,sh.moveChunk()不支持该选项 - 确认分片键索引存在:对
mydb.users执行db.users.getIndexes(),确保有以分片键开头的索引,且状态为"ready" - 确认没有正在进行的其他 chunk 操作:查
db.changelog.find({what: "moveChunk"}).sort({time: -1}).limit(5),避免重叠操作
最常被忽略的是哈希分片键场景下的 bounds 替代方案,以及 jumbo chunk 的 force 强制迁移路径——这两个点一旦出错,sh.moveChunk() 会直接报错退出,而错误信息里几乎不提示根本原因。


















