MongoDB生产环境不能依赖自动Balancer实时均衡,因其无自适应节流能力,一旦触发迁移会全力执行,占用网络带宽、锁住config server、阻塞分片写操作,易引发慢查询堆积和主从延迟飙升。

为什么 MongoDB 生产环境不能依赖自动 Balancer 做实时均衡
因为 Balancer 不是“后台守护进程”,它没有自适应节流能力,一旦触发迁移就会全力执行——哪怕正在处理 2000 QPS 的订单写入。迁移过程会占用大量网络带宽、锁住 config server 元数据、阻塞目标分片的写操作(尤其是 moveChunk 的三阶段提交),极易引发慢查询堆积甚至主从延迟飙升。
常见误判是看到 sh.getBalancerState() 返回 true 就以为“它很稳”。其实只要 sh.isBalancerRunning() 为 false,就说明当前没任务;但一旦有 chunk 不均,它可能在任意时刻突然启动,毫无预警。
- 迁移不是原子操作:单个
moveChunk默认超时 6 小时,期间无法中断 - 小 chunk 阈值(如设成 16MB)会显著增加迁移频次,导致 config server CPU 持续 >80%
- Balancer 完全不感知业务负载,不会因慢日志或队列积压而降速或暂停
如何用 cron 精确控制 Balancer 的启停时间
MongoDB 本身不提供 cron,必须靠操作系统调度。关键不是“能不能定时”,而是“怎么避免状态错乱”:
- 所有
mongosh命令必须连接到同一台mongos(比如mongos01:27017),否则sh.stopBalancer()在 A 节点执行后,B 节点可能因网络延迟短暂读到旧状态 - 务必加
--quiet参数,否则 cron 日志里会混入欢迎 banner 和提示信息,干扰故障排查 - 不要用
@daily这类模糊语法,明确写成0 2 * * *(每天 2:00)和0 4 * * *(每天 4:00)更可控 - 生产环境建议在 start/stop 后追加验证命令,例如:
sh.getBalancerState() && echo "OK"
示例(Linux cron):
0 2 * * * /usr/bin/mongosh --host mongos01:27017 --eval "sh.stopBalancer()" --quiet 0 4 * * * /usr/bin/mongosh --host mongos01:27017 --eval "sh.startBalancer()" --quiet
手动开启 Balancer 前必须确认的三件事
直接 sh.startBalancer() 很危险,尤其在扩容后。先做这三步检查:
- 运行
sh.status()查看是否有jumbo chunk:这类 chunk 因过大(文档数超限或 size 超 128MB)无法迁移,会卡死整个均衡流程 - 确认新分片磁盘空间充足:Balancer 不检查目标分片剩余空间,
moveChunk失败后会重试,形成雪球效应 - 检查分片键是否含稀疏唯一索引:这种组合会导致迁移时校验失败,错误信息类似
ConflictingOperationInProgress
如果发现 jumbo chunk,得先用 sh.splitAt() 手动拆分,再等 Balancer 接管后续迁移。
chunkSize 调小真能加快均衡?别被直觉骗了
把 chunkSize 从 64MB 改成 32MB,看起来迁移粒度更细,但实际效果往往相反:
- 每个 chunk 迁移都要走完整的复制 → 校验 → 清理流程,小 chunk 意味着更多轮元数据更新,config server 压力翻倍
- WiredTiger 的 page cache 会被大量小 chunk 的随机读写打散,反而拖慢整体吞吐
- 默认迁移阈值是 “某分片 chunk 数比平均值高 2 个以上”,改小 chunkSize 后,相同数据量 chunk 数翻倍,更容易频繁触发迁移
真正有效的做法是:保持默认 64MB,用 sh.moveChunk() 定向迁移热点 chunk,再让 Balancer 处理剩余部分。这样既可控,又不冲击 config server。
最常被忽略的一点:Balancer 状态写在 config.settings 集合里,但 activeWindow 字段在 MongoDB 3.4+ 已废弃。现在只认 enabled 布尔值——你往里面写 start/stopTime,它根本不会读。











