必须为分片键创建复合分片索引,如{region:1,userid:"hashed"},前缀字段region需高基数且支持范围查询,哈希字段userid须高基数、非单调;单调字段前置会导致写入热点;索引须在shardcollection前创建,方向对哈希字段无效但影响前缀扫描效率;需验证chunks跨分片分布及索引存在。

要在MongoDB分片集群中让数据均匀分布、查询可命中、块可持续拆分,必须为分片键创建结构合理且符合业务读写特征的复合分片索引。
确认分片键字段组合与顺序
先明确你要用作分片键的字段组合,例如 {region: 1, userId: "hashed"}。前缀字段(region)需具备足够高的关联基数且支持常见范围查询;哈希字段(userId)必须是高基数、非单调增长的字段,用于打散写入热点。
⚠️ 若将 单调递增字段(如时间戳、自增ID)放在哈希位置之前,会导致新文档持续写入同一分片——因为前缀范围固定时,哈希值再均匀也无法改变路由到同一 chunk 的事实。
确保所有候选字段已在集合中存在实际数据,且无大量 null 或空值;否则该值会被统一归入同一个哈希桶,引发数据倾斜。
创建复合分片索引
在主分片(或 config server 可写节点)上执行:db.collection.createIndex({region: 1, userId: "hashed"})。
这一步必须在调用 sh.shardCollection() 之前完成,否则 MongoDB 会拒绝分片操作并报错 “shard key not indexed”。
注意:索引方向对哈希字段无效,"userId": "hashed" 中的 1 或 -1 会被忽略;但对前缀字段(如 region)的方向会影响范围扫描效率,升序更利于时间/地域前缀的正向遍历。
执行分片并验证索引生效
第一步:启用数据库分片 → 第二步:运行 sh.shardCollection("mydb.users", {"region": 1, "userId": "hashed"})。
第三步:插入至少 3~5 条不同 region 值和不同 userId 值的测试文档 → 第四步:执行 sh.status() 查看 chunks 分布是否跨多个分片。
若只看到一个分片上有 chunks,说明分片键选择失败——大概率是 region 值过于集中,或 userId 实际基数极低(比如全为 0 或 null)。
第五步:在 mongosh 中运行 db.users.getIndexes(),确认输出中存在 {"region": 1, "userId": "hashed"} 类型的索引,且 key 字段精确匹配分片键定义。











