数据倾斜源于分片键或写入模式缺陷,如低基数或单调递增键导致写入集中;范围分片分裂滞后、哈希分片读负载不均、自动平衡器阈值未达均会引发负载不均衡,需结合sh.status()、db.collection.stats()及db.adminCommand验证并手动干预。

数据倾斜不是配置错了,而是分片键或写入模式触发了天然分布缺陷
分片键基数低或单调递增导致写入集中
比如用 _id(ObjectId)或时间戳字段做分片键,新文档总落在同一个 range 的末尾。MongoDB 的范围分片不会自动把热点 range 拆开,除非触发分裂条件(如 chunk 超过默认 128MB),而写入压力大时,分裂可能滞后甚至被 balancer 抑制。
- 典型表现:
sh.status()显示某分片 chunks 数远高于其他分片,但 size 却不一定最大(因为 chunk 大小不均) - 验证方式:运行
db.collection.stats()查看shardDistribution字段,再结合sh.status()中各分片的chunks和size对比 - 临时缓解:用
sh.splitAt("db.col", { ts: ISODate("2025-01-01") })主动切分热点区间,再配合moveRange迁出
哈希分片未解决查询局部性引发的读负载倾斜
哈希分片能均匀分布写入,但若业务查询总是集中在某个哈希前缀(例如固定用户组 ID 经哈希后落入同一 shard),读请求仍会打到少数分片上。此时 sh.status() 看起来数据量均衡,但 mongostat 或 db.currentOp() 会暴露某几个分片的 netIn 和 query 明显偏高。
- 关键区别:数据量均衡 ≠ 请求负载均衡
- 检查方法:在 mongos 上执行
db.adminCommand({ top: 1 }),观察各 shard 的reads/writes分布 - 优化方向:避免在应用层固化查询范围;必要时改用复合分片键,加入高基数字段打散
自动平衡器未触发迁移,因差值未达阈值
MongoDB 6.0.3+ 的 balancer 不按 chunk 数判断,而是看实际数据量差 —— 必须 ≥ 384MB(3 × 默认 chunk 大小)才启动迁移。小规模集群(比如总数据量才 1GB)即使各分片 size 差 200MB,balancer 也完全沉默。
- 常见误判:看到
sh.getBalancerState()返回true就以为“它在干活”,其实可能根本没触发条件 - 确认手段:运行
db.adminCommand({ balancerCollectionStatus: "db.collection" }),若返回空或"migratingChunks": 0,说明当前无迁移任务 - 手动干预时机:刚加新分片后 size 为 0,老分片已满,但差值 moveRange 搬数据,别等
真正麻烦的是那种“看起来均衡、查着慢、监控看不出明显异常”的情况——往往卡在分片键与业务查询模式的隐性耦合上,得从 query pattern 反推数据分布,而不是只盯着 sh.status() 里的数字。











