高基数字段是分片键的必要门槛而非充分条件;需满足cardinality ≥ 分片数×3,且须结合写入时序与查询模式验证:用$group+$count查真实去重值,用sh.status()观察chunk分布,确保高频查询覆盖复合键前缀,并避免单调字段导致的伪高基数陷阱。

高基数字段本身不是分片键的充分条件,但它是必要门槛——cardinality 的字段,无论多“唯一”,都别硬上。
怎么快速验证字段基数是否达标
别靠猜,用聚合查真实去重值数量:
db.collection.aggregate([
{ $group: { _id: "$your_field" } },
{ $count: "distinct_count" }
])
结果必须 ≥ 当前分片数 × 3。比如你有 6 个分片,那 distinct_count 至少得 > 18;若只有 5,哪怕字段值全不重复(如 status 只有 5 个枚举),也直接淘汰。
- 注意:
_id看似唯一,但若业务层用毫秒时间戳拼接或自增整数,实际分布会塌缩成单调递增序列,distinct_count高 ≠ 写入分散 -
city字段全国 50 个城市 → 基数 50 → 仅支持最多约 16 个分片,再多无效 - 用
db.collection.stats()看shards下各分片的chunks数量,偏差超 ±15% 就说明基数没真正起作用
高基数 ≠ 写入均匀,关键看字段和写入时序是否“相干”
一个字段能返回百万级 distinct_count,但若它和写入时间强绑定(比如 created_at、log_id 自增),新文档仍会全部挤在最新 chunk 上——这就是“伪高基数”陷阱。
- 验证方法:
db.collection.find().sort({ your_field: -1 }).limit(1)和.sort({ your_field: 1 }).limit(1)查跨度,再对比sh.status()中各分片 chunk 数是否严重失衡 - 补救不是加索引,而是打散:改用
{ your_field: "hashed" },或组合如{ region: 1, your_field: "hashed" }(前提是region本身高基数且稳定) - 绝对避免单独对
Date类型字段做范围分片,尤其搭配 TTL 删除时,空洞会固化为磁盘垃圾
复合分片键里,高基数字段必须是“前缀”且“高频参与查询”
MongoDB 路由依赖复合键前缀匹配。如果高频查询只带 user_id,但分片键是 { order_date: 1, user_id: 1 },mongos 就无法定位到单一分片,退化为全分片广播扫描。
- 必须保证:80% 以上查询条件包含复合键最左字段(即前缀)
- 哈希字段只能放在末尾:
{ tenant_id: 1, _id: "hashed" }合法,{ _id: "hashed", tenant_id: 1 }语法错误 - 第二字段不能是“假分散”:比如批量导入时所有
order_date都填"2026-07-21",再高基数也白搭
真正难的不是找一个高基数字段,而是确认它在真实写入流量下是否持续发散、在真实查询模式下是否可路由——这两点漏掉任何一项,分片集群都会悄悄退化成单点瓶颈。











