sh.setBalancerWindow()用于设置MongoDB均衡器时间窗口,如sh.setBalancerWindow({start:"23:00",end:"05:00"}),使迁移仅在指定本地时区时段运行,配置持久化于config.settings且下次检查周期(默认10秒)生效。

如何用 sh.setBalancerWindow() 设置均衡器时间窗口
MongoDB 均衡器默认全天候运行,一旦分片间数据不均,就会立刻触发迁移——这在业务高峰期可能拖慢查询、占满网络和磁盘 IO。最直接的控制方式是设置时间窗口,让迁移只在指定时段发生。
执行前需确保你有 clusterAdmin 权限,并连接到 mongos 实例:
sh.setBalancerWindow({ start: "23:00", end: "05:00" })
这个命令会让均衡器只在晚上 11 点到凌晨 5 点之间启动迁移任务。注意:start 和 end 是本地时区(即 mongos 所在服务器的系统时区),不是 UTC,也不是配置文件里写的时区。
-
start和end必须用"HH:MM"格式,不能带秒、不能用 24 小时外的值(比如"25:00"会报错) - 如果
end小于start(如上例),表示跨天窗口;MongoDB 内部会自动处理,不用手动拆成两个区间 - 该设置保存在
config.settings集合中,重启 mongos 不丢失,但修改后不会立即生效——下一次均衡器检查周期(默认 10 秒)才会读取新窗口
为什么停不掉 sh.stopBalancer() 不适合日常节流
有人看到“停用均衡器”就立刻执行 sh.stopBalancer(),结果发现集群慢慢失衡,甚至某个分片磁盘打满。这不是 bug,是设计使然:停用只是暂停迁移调度,不暂停正在进行的迁移任务,也不阻止 chunk 拆分或应用写入导致的数据倾斜。
更关键的是,停用期间如果发生分片宕机或添加新分片,均衡器无法自动恢复平衡,人工介入成本陡增。
-
sh.stopBalancer()是紧急兜底手段,比如正在做备份、升级或排查严重性能抖动,且你明确知道后续要人工干预 - 它不接受时间参数,也没有“暂停到几点”的选项;恢复必须显式调用
sh.startBalancer() - 停用超过 4 小时,
config.migrationHistory可能被轮转清理,影响问题回溯
检查当前均衡器状态和窗口配置
别只信自己记得设过窗口。生产环境常因配置同步延迟、权限误操作或脚本未生效,导致实际运行状态和预期不符。
查是否启用:
sh.getBalancerState()
查当前窗口(返回 null 表示未设置):
db.getSiblingDB("config").settings.findOne({ _id: "balancer" })
查最近一次迁移是否在窗口内(辅助验证):
db.getSiblingDB("config").migrationHistory.find().sort({ time: -1 }).limit(1)
- 如果
sh.getBalancerState()返回true,但config.settings.balancer里没有activeWindow字段,说明窗口未设置,均衡器仍在全时段工作 - 迁移历史中的
time是 BSON UTC 时间戳,需换算成 mongos 本地时区对比窗口 - 某些旧版本 MongoDB(如 3.6 以前)不支持
setBalancerWindow,调用会报unrecognized command错误
窗口之外迁移仍可能发生的三个例外场景
设置了窗口 ≠ 绝对安静。以下情况仍可能触发迁移,且不受窗口限制:
- 手动执行
sh.moveChunk():这是强制操作,绕过所有策略检查 - 自动 chunk 拆分后引发的“必要迁移”:比如某 chunk 超过 64MB 触发拆分,新 chunk 被分配到负载更低的分片,这个过程不走均衡器调度器,也不看窗口
- 分片加入或移除时的初始再平衡:只要集群拓扑变化,MongoDB 会立刻启动一次强制再平衡,不管窗口
这些例外意味着:窗口只是控流主力,不是隔离墙。真正压测或核心交易时段,建议配合降低 chunkSize(减少单次迁移数据量)、监控 moveChunk 日志、以及提前规划好分片容量余量。
最容易被忽略的是时区——所有人盯着监控看凌晨 2 点有没有迁移,却没确认 mongos 服务器的 date 输出是否真和你的“业务低峰”一致。一次时区偏差,就等于把窗口开在了白天。


















