分片键选择不当会导致chunk分布严重倾斜,如某分片chunk数超其他3倍即存在热点;单调递增或低基数分片键(如时间戳、少量status值)是主因;新写入集中于末尾chunk表明离散性差。

看 chunk 分布是否严重倾斜
分片键选得对不对,最直接的证据就是 sh.status() 输出里各分片的 chunk 数量。如果某个分片 chunk 数量是其他分片的 3 倍以上,基本可以判定存在热点——数据没散开,写入和查询都压在少数节点上。
执行 sh.status() 后重点看 chunks 列,比如:
mydb.mycoll shard01: 1245 chunks shard02: 132 chunks shard03: 98 chunks
这种分布不是“不均匀”,而是“失效”。背后原因通常是分片键单调递增(如时间戳、自增 ID)或低基数(如 status 只有 3 个值)。
查写入是否集中在最后几个 chunk
用 db.mydb.mycoll.getShardDistribution()(MongoDB 4.4+)或手动查 config.chunks,看最新写入落在哪些 chunk 区间。如果连续多个新文档都落到同一个 chunk 或相邻 chunk,说明分片键缺乏离散性。
常见陷阱:
- 用
createdAt当分片键 → 所有新数据进最后一个 chunk - 用
tenant_id但租户数量极少( - 哈希分片用了低基数字段(如布尔值)→ 实际只生成 2 个哈希桶,chunk 全挤在两个分片上
观察广播查询是否频繁出现
当查询不带分片键时,mongos 会向所有分片广播。如果 db.currentOp({secs_running: {$gt: 5}}) 里大量出现 "desc" : "doing a scatter gather",说明业务查询经常无法路由到单一分片。
这不是代码写错了,而是分片键和查询模式错配。例如:
- 分片键是
{_id: "hashed"},但业务查询总按user_id查 → 每次都广播 - 分片键是
{region: 1, timestamp: 1},但查询只传timestamp→ 无法定位 chunk 区间
此时重构分片键比加索引更治本——索引解决的是“查得快”,分片键解决的是“往哪查”。
检查 jumbo chunk 是否持续增长
chunk 大小超过阈值(默认 64MB)且无法分裂,就会变成 jumbo chunk。它卡在某个分片上不动,既不能迁移,又拖慢平衡器。执行 sh.status() 时如果看到 jumbo: true 标记长期存在,大概率是分片键导致数据无法切分。
典型原因:
- 分片键值完全相同(如全集合
type: "log"字段恒定)→ 所有文档 hash 到同一值,只能塞进一个 chunk - 范围分片键的最小/最大值太接近(如所有
order_id都在 1000000~1000005 范围内)→ chunk 边界无法有效划分
这类问题无法靠 reshardCollection 解决——它要求新分片键字段在所有文档中非空且有足够离散度,而 jumbo chunk 往往意味着旧键本身就不满足这个前提。
真正难处理的不是“要不要重构”,而是“能不能安全重构”。reshardCollection 的写锁、双键约束、磁盘空间要求,让很多团队卡在“知道有问题却动不了”的状态。这时候得先确认:现有分片键是否已造成不可逆的性能劣化?还是还能靠应用层路由+索引优化扛一阵?毕竟重构一次的成本,可能抵得上半年的运维调优。











