调用 _sh.stopBalancer() 前必须检查集群状态:确认 balancer 开启(sh.getBalancerState())、无活跃迁移(sh.isBalancerRunning())、config 库无残留迁移任务;该命令仅阻止新迁移,不中断进行中的 chunk 迁移。

调用 _sh.stopBalancer() 之前必须确认分片集群状态
直接执行 _sh.stopBalancer() 不会报错,但若集群正在做 chunk 迁移、有未完成的迁移任务或 config server 不可用,命令看似成功,实际 balancer 可能仍在后台尝试工作,甚至导致后续 _sh.startBalancer() 失败。务必先检查当前状态。
- 运行
sh.getBalancerState()确认 balancer 当前是true(开启)还是false(已停) - 运行
sh.isBalancerRunning()查看是否有迁移任务正在执行 —— 返回true表示有活跃迁移,此时停 balancer 无法中断正在进行的迁移,只能阻止新任务入队 - 用
db.getSiblingDB("config").getCollection("migrationTasks").find().count()检查 config 库中是否残留迁移任务文档(旧版本 MongoDB 可能遗留)
_sh.stopBalancer() 不是立即生效的“硬暂停”
它只是设置 balancer 开关为 false,并通知所有 mongos 停止调度新迁移;但已在执行中的 chunk 迁移不会被中止,会继续跑完。这点常被误认为“一调就停”,结果业务观察到数据还在动,以为命令失效。
- 迁移一旦开始(
startedAt已写入config.migrations),就必须等它完成或失败,balancer 控制不了生命周期 - 停 balancer 后,可通过
db.getSiblingDB("config").migrations.find({ "state": "committed" }).count()观察已完成迁移数,确认无进行中任务 - 如果发现迁移卡住(如状态长期为
catching_up或cloning),需人工介入,比如 kill 对应 migration 的moveChunk操作(通过db.currentOp()找出)
停 balancer 期间要警惕 chunk 分布倾斜和查询性能退化
均衡停止后,新增写入集中在某些分片(尤其是按时间字段分片时),会导致该分片磁盘/内存/CPU 快速上涨,而其他分片闲置。这不是 balancer 的问题,但却是停用后最常引发线上告警的连锁反应。
- 用
sh.status()定期看各 shard 的 chunk 数分布,重点关注最大最小差值是否超过 20%(经验值) - 检查热点集合的
sh.status("db.coll"),确认是否存在单个 shard 承载了 80%+ 的 chunk - 如果业务允许,可在停 balancer 前手动预拆分(
sh.splitAt())或移动部分 chunk(sh.moveChunk())来临时平衡负载 - 注意:
sh.moveChunk()本身会触发一次迁移,需确保 balancer 已停,否则可能被 balancer 干扰
恢复 balancer 前必须清理残留锁和验证 config server 可写
_sh.startBalancer() 失败最常见的原因是 config server 上还留着 balancer lock 文档(config.locks._id == "balancer"),或者 lock 被标记为过期但没被自动清除。这种情况下命令返回成功,实际 balancer 并未启动。
- 停用期间若发生异常(如 mongos crash、网络分区),lock 文档可能处于
state: "locked"且who字段指向已不存在的进程 - 恢复前先查:
db.getSiblingDB("config").locks.findOne({ "_id": "balancer" }),若存在且state !== "unlocked",需手动更新:db.getSiblingDB("config").locks.updateOne({ "_id": "balancer" }, { $set: { "state": "unlocked", "who": "", "ts": ObjectId() } }) - 确认 config server 主节点可写:
db.getSiblingDB("config").runCommand({ "ping": 1 })成功且无 write concern 错误 - 执行
_sh.startBalancer()后,立刻用sh.isBalancerRunning()和db.getSiblingDB("config").locks.findOne({ "_id": "balancer" })双重验证










