分片键基数太低导致数据倾斜可快速验证:先用db.collection.distinct("shard_key_field").length检查唯一值数量,若远小于1000(百万级集合)即基数不足;再用聚合查看分布,若前10项count占比超30%则已倾斜。

分片键基数太低导致数据倾斜,怎么快速验证?
MongoDB 分片集群里,sh.status() 显示某个 chunk 特别大、而其他分片几乎空着,基本就是分片键基数不足或分布不均。先确认是不是这个原因:用 db.collection.aggregate() 查分片键的唯一值数量和分布直方图。
实操建议:
- 执行
db.collection.distinct("shard_key_field").length—— 如果结果远小于 1000,说明基数太低(尤其当集合有百万级以上文档时) - 跑个简单聚合看分布:
db.collection.aggregate([{$group: {_id: "$shard_key_field", count: {$sum: 1}}}, {$sort: {count: -1}}, {$limit: 10}]),如果前几项count占比超 30%,大概率已倾斜 - 注意:
_id作为默认分片键时,若应用用的是自增整数或时间戳,会天然导致写入集中在最新 chunk,这就是典型“单调递增键陷阱”
为什么 Hashed Shard Key 不总能解决问题?
Hashed 分片键(如 {field: "hashed"})能打散写入热点,但代价是牺牲范围查询能力,且对基数极低的字段无效——比如字段只有 3 个取值,哈希后还是最多 3 个 chunk,无法真正分散。
关键点:
- 哈希只改变分布方式,不增加基数;原始字段若只有
"A"/"B"/"C",哈希后仍是 3 个分片键值,数据仍卡在 3 个 chunk - 所有范围查询(
{$gt: x, $lt: y})变全分片扫描,性能跳崖式下降 - 复合分片键中混用 hashed + ranged(如
{tenant_id: "hashed", created_at: 1})看似兼顾,但若tenant_id基数低,依然会聚堆——每个tenant_id的哈希值固定,其下所有created_at全落在同一个分片
重新选取 Shard Key 的实际操作步骤 不能直接改分片键,必须重建集合。核心是:停写 → 导出 → 新建带合理分片键的集合 → 重分片 → 导入 → 切流。
注意事项:
- 新分片键首选高基数、写入均匀、查询高频的字段,例如用户系统中
user_id(UUID 或雪花 ID)比status(枚举值)靠谱得多 - 避免用含大量
null或空字符串的字段——MongoDB 把所有null归为同一分片键值,极易倾斜 - 执行
sh.shardCollection("db.coll", {"new_shard_key": 1})前,确保该字段在所有文档中存在且类型一致;否则会报错cannot shard collection with missing or inconsistent shard key value - 导入时用
mongorestore --drop,别漏掉--drop,否则旧数据残留引发逻辑混乱
上线后如何持续监控是否又歪了? 倾斜不是一劳永逸的事。业务增长、字段分布变化、归档策略调整都可能让原本健康的分片键逐渐失效。
盯住这几个信号:
- 定期跑
sh.status(),重点看各分片的chunks数量和大小是否偏离均值 ±20% - 查
db.collection.getShardDistribution()(4.4+),它直接返回每分片文档数占比,比肉眼数 chunk 更准 - 留意
moveChunk日志频率——如果某分片频繁接收 chunk 迁入,说明它正在成为“倾倒区” - 特别小心 TTL 索引清理后的空洞:旧 chunk 没被及时合并,表面均衡,实际写入全挤在少数活跃 chunk,用
sh.printShardingStatus()看size和docs是否同步萎缩
region 分片,后来发现 80% 请求来自一个 region,那这个键就废了——这时候不是调参数能救的,得回到数据模型本身。










