应避免用 company_id + create_time 复合哈希分片键,因 company_id 区分度低易致数据倾斜;若查询多含 create_time 范围条件,应将其设为前缀,搭配高区分度字段哈希,如 {create_time: 1, user_id: "hashed"},并用 analyzeshardkey 验证分布与广播风险。

复合哈希分片键到底该不该用 company_id + create_time?
不能直接套用。线上案例里用 company_id 哈希分片导致范围查询变慢,根本原因不是哈希本身,而是把高基数但低区分度的字段(比如大量公司共用同一个 company_id)当成了唯一分发依据。复合哈希分片键的前缀字段必须能支撑业务常见查询模式——如果 80% 查询都带 create_time 范围条件,那它更适合做前缀,而不是哈希部分。
正确做法是:前缀字段选支持范围/等值混合查询的高区分度字段,哈希字段补足数据倾斜。例如:{region: 1, user_id: "hashed"},其中 region 支持按地域路由和范围扫描,user_id 哈希保证单 region 内写入均匀。
- 哈希字段必须是单值、非数组、非浮点(或确保 ≤2⁵³)
- 前缀字段不能是单调递增字段(如
_idObjectId),否则仍会热点 - 避免在哈希字段上建额外普通索引(如同时有
{"company_id": "hashed"}和{"company_id": 1}),mongos 可能误选非哈希索引导致广播查询
为什么 analyzeShardKey 比手动拍脑袋更可靠?
MongoDB 7.0 的 analyzeShardKey 不是“看看就行”的工具,它基于真实采样查询生成分布热力图和冲突预测。比如你传入 {company_id: "hashed", create_time: 1},它会告诉你:
- 该键下预估 chunk 数量是否超过 1000(过大影响迁移效率)
- 是否存在 >5% 的 chunk 数据量偏差(说明哈希后仍不均)
- 哪些查询会被迫广播(比如
find({create_time: {$gt: ...}})在哈希前缀缺失时)
执行示例:
db.visitor.analyzeShardKey({company_id: "hashed", create_time: 1}, {sampleSize: 100000}) 返回结果中重点关注 imbalancedChunks 和 broadcastOperations 字段值。
哈希分片后,explain("executionStats") 怎么看才不被误导?
哈希分片集群中,explain 的 executionStats 显示的是单个分片的统计,不代表全集群耗时。真正关键的是 executionStages.shards 下各分片的 executionTimeMillisEstimate 分布——如果某一分片耗时远高于其他(比如 1200ms vs 其余均
- 看到
stage: "SHARD_MERGE"是正常现象,但若伴随大量docsExamined,说明 mongos 没准确定位到目标分片,可能用了错误索引 -
totalKeysExamined接近totalDocsExamined,大概率没走哈希索引,检查查询是否为等值(company_id: "abc123")而非范围(company_id: {$in: [...]}) - 哈希分片下
IXSCAN阶段的keyPattern必须显示"hashed",否则 mongos 正在用普通索引做广播扫描
7.0 新增的 checkMetadataConsistency 解决什么实际问题?
元数据不一致不会立刻报错,但会导致查询路由错误——比如某个 chunk 的元数据在 config server 中标记为在 shardA,实际数据却在 shardB,mongos 就会查不到或查重。7.0 的 checkMetadataConsistency 能主动发现这类静默故障。
建议在每次分片变更(如添加新分片、refineCollectionShardKey)后立即运行:
sh.checkMetadataConsistency({checkIndexes: true}) 它比人工比对 config 数据库快得多,且能定位到具体 collection 和 chunk。
最容易被忽略的一点:该命令不校验数据内容一致性,只校验路由元数据。如果你怀疑某次迁移后数据丢失,得配合 countDocuments 在各分片单独执行比对。











