balancer挂起主因是长事务阻塞movechunk操作,而非事务本身;需通过db.currentop()查长时运行操作、config.changelog缺commit记录及sh.status()中chunk分布失衡等交叉验证。

Balancer 会因长事务挂起,但根本原因不是事务本身,而是它阻塞了 moveChunk 操作
当某个分片上存在长时间运行的写事务(比如未提交的批量更新、大范围 updateMany 或带 $isolated 的操作),moveChunk 命令会被拒绝执行——因为迁移必须保证数据一致性,而活跃事务会让目标分片无法安全接收新 chunk。此时 Balancer 不会报错退出,而是进入“等待重试”状态,表现为 sh.getBalancerState() 显示 true,但 sh.status() 中 chunk 分布长期不动。
这种挂起不会立刻暴露:监控面板可能仍显示 “Balancer running”,日志里也难见明显错误。但后果是数据倾斜持续加剧,热点分片磁盘和 CPU 逐步拉满,直到触发 ChunkTooBig 或查询超时。
如何确认是不是被长事务卡住
- 在 mongos 上执行
db.currentOp({secs_running: {$gt: 30}}),重点看secs_running超过 60 秒且command.moveChunk相关的操作 - 连接到疑似卡住的分片主节点,查
db.currentOp({ns: "config.chunks", secs_running: {$gt: 10}})—— 如果 config server 上也有长时间 pending 的 chunk 元数据更新,基本坐实 - 检查
config.changelog最后几条记录:如果连续多条都是split或moveChunk start,但没有对应的moveChunk commit,说明迁移卡在中间
定期检查 Chunk 分布,本质是检查 Balancer 是否真在干活
不能只看 sh.getBalancerState() 返回 true 就放心。要结合 sh.status() 输出中各分片的 chunk 数差异、最大 chunk size、以及最近 1 小时内 config.changelog 的操作频率来交叉判断。
例如:某分片 chunk 数比平均值高 40%,且过去 2 小时 config.changelog 中只有 1 条 moveChunk 记录,大概率已被挂起。这时得人工介入:杀掉长事务、临时停 Balancer(sh.stopBalancer())、再手动 sh.moveChunk() 搬走最重的 chunk。
真正容易被忽略的一点:Balancer 挂起后,自动 split 也会停摆
chunk 分裂依赖 Balancer 的协调流程。一旦它卡在 moveChunk 环节,即使有 chunk 超过 chunkSize,也不会触发自动分裂——你看到的 ChunkTooBig 往往是挂起数小时甚至数天后的结果,而非突发写入导致。所以检查分布不是为了“看看图”,而是为了及时发现那个早已沉默的 Balancer。











