sh.status()中balancer部分仅显示是否启用、是否运行中、最近成功迁移时间;不反映待迁移chunk、队列状态、延迟或分布偏差,诊断需查config.migrationHistory、mongos日志及config.chunks聚合统计。

许多初次接触分片集群的开发者在看到sh.status()输出中的balancer部分时,往往误以为这就是平衡器的全貌。实际上,这里隐藏着不少细节与陷阱。今天我们就深入剖析,明确哪些信息可靠可用,哪些需要转向其他数据源去挖掘。
sh.status() 返回的 balancer 部分究竟提供了哪些信息
查看 sh.status() 输出里的 balancer 段,核心信息仅包含三要素:是否启用、是否正在运行、最近一次成功迁移的时间戳。它并不会告知你“下一步要迁移哪块 chunk”,也不会展示排队中的迁移任务——这些内容需要查询 config 数据库或查看 mongos 日志才能获取。
常见误区是将 "enabled": true 理解为“此刻正在进行数据迁移”,其实它仅仅表示平衡器开关已打开;而 "currentlyRunning": true 才表明有迁移任务正在执行(即便只有一条)。
"enabled": false代表平衡器被手动停止(通过sh.stopBalancer()),即使集群严重数据倾斜也不会自动处理"currentlyRunning": true但"activeWindow": null,意味着没有设置活动窗口,平衡器全天候持续工作- 若
"lastSeen": "..."时间戳超过 1 小时未更新,大概率迁移过程出现卡住,需排查config.migrationHistory或mongos日志中的moveChunk错误
活动窗口(activeWindow)并非定时任务,而是一个白名单时间段
activeWindow 是平衡器允许执行迁移操作的时间范围,并非类似 cron 的定时调度机制。它仅控制迁移启动的时机,不会中断已在进行的迁移过程。一旦迁移开始,即便超出窗口设定的时间,也会继续完成。
配置方式是通过调用 sh.setBalancerWindow(),并传入 UTC 时间段(注意不是本地时区):
sh.setBalancerWindow( { start: "22:00", stop: "06:00" } )
该配置存储在 config.settings 集合中,所有 mongos 实例共享。如果多个 mongos 同时执行该命令,后写入的会覆盖前写入的——没有合并逻辑。
- 时间格式必须严格为
"HH:MM",不支持秒、时区缩写或 AM/PM - 跨日区间(如
"22:00"到"06:00")是合法的,内部按照 UTC 处理 - 删除窗口只需设置为
null:sh.setBalancerWindow(null),不要使用空对象 - 修改后不会立即生效,下一个平衡周期(默认每 10 分钟检查一次)才会读取新配置
为什么 sh.status() 里看不到迁移延迟或 chunk 分布偏差
sh.status() 的 balancer 块本质上是状态快照,而非监控仪表盘。它不包含实时负载、延迟数值、chunk 数量差值等指标——这些需要自行计算获得。
例如,要判断集群是否失衡,不能仅依赖 "currentlyRunning": false,还需要检查各 shard 的 chunk 总数:
db.getSiblingDB("config").chunks.aggregate([
{ $group: { _id: "$shard", count: { $sum: 1 } } }
])
再结合 db.getSiblingDB("config").tags.find() 查看分片标签规则,才能确认是真正的数据倾斜还是策略导致的“假失衡”。
- 平衡器默认每 10 分钟触发一次检查,但实际迁移频率受
chunkSize、网络延迟、目标 shard 磁盘压力等因素影响,sh.status()不会反映这些动态 - 如果某次
moveChunk失败(例如目标 shard 写入已满),错误信息仅记录在mongos日志和config.migrationHistory中,sh.status()里只会显示"lastSeen"时间停滞 sh.status()输出的"migrationCount"是历史累计值,并非当前队列长度,无法用于判断迁移任务的积压情况
停用平衡器时,sh.stopBalancer() 和直接修改 config.settings 效果不同
sh.stopBalancer() 提供安全停用流程:它首先等待当前正在进行的迁移完成,然后更新 config.settings 并广播给所有 mongos 实例。而直接手动 update config.settings 中的 balancer 文档,虽然会立即生效,但可能中断正在执行的迁移,导致数据不一致或锁残留。
恢复时同理:sh.startBalancer() 会校验集群健康状态(例如所有 shard 是否可达),而直接修改 "enabled": true 不做任何检查。
- 生产环境中停用平衡器务必使用
sh.stopBalancer(),不要绕过 shell 封装 - 停用期间新增的 chunk(如通过 bulk insert 操作)不会自动被切分,直到平衡器重启后才会参与均衡——这一点常被忽略
- 如果
sh.stopBalancer()卡住超过 30 秒,大概率存在迁移阻塞,需要检查config.locks中"_id": "balancer"的state字段是否等于2(表示被其他操作锁定)
平衡器的状态看似简单,但 sh.status() 隐藏了大量上下文依赖:时间窗口的 UTC 语义、迁移中断的边界条件、停用操作的原子性保障。若真要用它诊断问题,必须随时准备好翻阅 config.migrationHistory 和 mongos 日志。
