Balancer开着却不迁移数据块,是因为“enabled:true”仅表示开关开启,真正执行需满足三条件:currentlyRunning为true、无jumbo chunk阻塞、config.locks中无残留aborting锁。

Balancer 明明开着,却不动数据块——这不是 bug,是它被拦住了
检查 sh.status() 里 balancer 字段是否真在干活
很多人看到 sh.status() 输出里 "enabled": true 就以为迁移会自动发生,其实这只是开关状态。真正决定“此刻有没有在搬”的是 "currentlyRunning": true。如果它是 false,哪怕集群严重倾斜,Balancer 也不会启动新迁移任务。
-
"enabled": false表示被手动停用(sh.stopBalancer()),不会响应任何倾斜 -
"currentlyRunning": false但"enabled": true,说明当前没任务排队,可能刚跑完、或还没触发检查周期(默认每 10 分钟轮询一次) -
"lastSeen"时间戳超过 1 小时没更新,大概率卡住了,不是“不干活”,而是“干不了”
巨型块(jumbo chunk)直接让迁移停摆
Balancer 遇到标记为 jumbo 的 chunk 会跳过,不报错、不重试、也不拆分——它只负责迁移,不分割。一旦出现 jumbo,对应范围的数据就彻底“冻结”在原分片上。
- 查 jumbo:
sh.status(true)输出中搜索jumbo关键字 - 可分割的 jumbo:包含多个唯一分片键值,用
sh.splitAt("db.coll", { shardKey: value })手动切开 - 不可分割的 jumbo:单一分片键值撑满整个 chunk,必须改分片键(
refineCollectionShardKey)或重新分片 - 注意:MongoDB 6.0.3+ 已停用自动分割,
sh.splitFind等命令不再生效
残留的 aborting 迁移锁阻塞后续所有操作
迁移中途断电、网络中断或 config server 写入失败,会导致 config.migrations 里留下 state: "aborting" 或 "aborted" 文档,同时 config.locks 中的 _id: "balancer" 或 _id 匹配 /moveChunk/ 的锁未释放。Balancer 检测到锁存在,就拒绝启动新任务。
- 确认残留:
db.getSiblingDB("config").migrations.find({ state: { $in: ["aborted","aborting"] } }) - 清理必须连 config server 主节点(不是 mongos),且需
clusterAdmin权限 - 顺序不能错:先
sh.stopBalancer()→ 删 migrations → 删 locks →db.adminCommand({ flushRouterConfig: 1 })→sh.startBalancer() - 删完后原迁移不会回滚,若目标分片已有数据,得人工核对是否产生孤儿文档
活动窗口(activeWindow)设了等于没设
activeWindow 是白名单机制,不是定时器。它只控制“允许新迁移开始的时间段”,不中断已在执行的任务。但如果窗口配置错误,Balancer 就永远等不到启动时机。
- 时间必须是 UTC 格式
"HH:MM",不支持"22:00:00"或"10PM" - 跨日如
{ start:"22:00", stop:"06:00" }合法,但写错成"06:00"到"22:00"就无效 - 多个 mongos 同时调
sh.setBalancerWindow(),后执行的会覆盖前执行的——没有合并逻辑 - 修改后不会立刻生效,要等下一个平衡检查周期(约 10 分钟)才读取新配置
最常被忽略的是:Balancer 不看负载、不看延迟、不看 chunk 数量差值。它只认三件事——开关开了没、锁清干净没、chunk 能不能搬。所有“为什么不动”的问题,几乎都落在这三个点上。











