自动平衡器默认开启但非全自动,mongodb 6.0.3+按实际数据量差≥384mb触发迁移,而非实时或按chunk数量均衡。

自动平衡器默认开启,但不等于“全自动”
从 MongoDB 5.0 开始,balancer 默认启用,但它的行为受迁移阈值、块(或范围)大小、分片间数据量差值等多重条件约束。它不会一添加新分片就立刻把所有数据匀开,也不会在每秒都做迁移。你看到 sh.status() 中各分片 size 差距仍较大,不是平衡器坏了,而是还没触发迁移条件。
关键判断点:MongoDB 6.0.3+ 版本中,迁移阈值是按**实际数据量**(而非 chunk 数量)计算的——当最大分片与最小分片的数据量差 ≥ 384MB(即 3 × 默认 range 大小 128MB),才会启动一轮均衡。这意味着小规模集群(总数据量
- 用
sh.status()查看当前各分片的size和chunks数,注意区分“块数量均衡”和“真实数据量均衡” - 用
db.adminCommand({ balancerCollectionStatus: "db.collection" })查看指定集合是否正在迁移、剩余多少 range 待处理 - 执行
sh.getBalancerState()确认 balancer 当前是 true 还是 false;若为 false,需手动运行sh.setBalancerState(true)
什么时候必须手动干预?看这三种典型场景
自动平衡器擅长渐进式微调,但面对业务突发、架构变更或数据倾斜时,响应太慢甚至无法解决。以下情况建议主动出手:
-
刚加完新分片:新分片
size为 0,老分片满载,但差值未达 384MB → 平衡器不动作。此时可用moveRange(6.0.3+)或moveChunk(旧版)把高流量区间的 range 手动迁出 -
某分片持续成为热点:比如分片键设计不合理,导致大量写入集中到单个 range(如时间戳递增),自动分裂只在迁移时发生 → 需先用
sh.splitAt()或sh.splitFind()主动切分热点 range,再配合moveRange分流 -
要紧急下线一个分片:自动清空流程可能耗时数小时甚至更久,且期间该分片仍参与路由 → 应先用
removeShard触发清空,再用sh.waitForBalancer()等待迁移完成,避免半途失败
手动命令与自动策略共存的实操要点
手动命令不是绕过平衡器,而是与它协同工作。MongoDB 会把手动迁移视作一次“合法迁移事件”,并据此更新内部状态,影响后续自动决策。
-
moveRange命令(6.0.3+)要求显式指定bounds(范围上下界)、fromShard和toShard,不能只给 collection 名;错误示例:moveRange("db.col", { min: {}, max: {} })缺少fromShard会报错 - 执行手动迁移前,建议先禁用 balancer(
sh.setBalancerState(false)),防止自动任务和人工操作冲突;迁移完成后立即恢复(sh.setBalancerState(true)) - 手动迁移不影响其他集合的自动平衡,但同一集合上并发手动操作会被排队;可通过
db.adminCommand({ listCurrentOperations: 1 })查看是否有moveRange正在运行
监控与验证不能只看 sh.status()
sh.status() 显示的是元数据快照,滞后且不反映实时迁移压力。真正判断“是否平衡”,得交叉验证三类指标:
- 各分片的
db.stats().dataSize—— 实际存储字节数,比sh.status()的size更准(后者含索引) - 配置服务器上的
config.chunks集合文档数 —— 可估算 range 总量,结合config.tags检查区域标签是否干扰迁移 - mongos 日志中是否频繁出现
moveRange: started/moveRange: success—— 这才是平衡器真正在干活的证据
最容易被忽略的是:平衡器迁移 range 时,会对源分片和目标分片产生 I/O 与网络压力,如果在业务高峰执行,可能引发查询延迟毛刺。所以哪怕自动平衡开着,也建议用 sh.setBalancerWindow() 锁定低峰期窗口(如 {start:"02:00",stop:"05:00"}),这个设置对自动和手动迁移都生效。











