上一篇讲了片键设计的三个目标,这一篇讲片键选好后,怎么分——范围分片还是哈希分片。这是同一个片键的两种分散策略,它们直接决定了:范围查询能不能定向、写入会不会热点。选错了,要么查询全广播,要么写入堆在一个 shard。

先把机制边界说清楚

MongoDB 把数据按片键划分成 chunk,划分方式有两种:

  • 范围分片(Ranged Sharding):按片键值的大小范围划分。比如片键是数值,[1,1000) 一个 chunk、[1000,2000) 一个 chunk。相邻的值在同一个 chunk(大概率同一 shard)。
  • 哈希分片(Hashed Sharding):按片键值的哈希划分。先算片键值的哈希,按哈希值范围划分。相邻的原始值,哈希后分散到不同 chunk。

两者的本质差别是:范围分片保留了片键的顺序性,哈希分片打散了顺序性。这个差别决定了它们各自擅长什么。

范围分片 vs 哈希分片

范围分片 vs 哈希分片

范围分片的优势是范围查询能定向。因为相邻的片键值在同一个 chunk/同 shard,查询 {createdAt: {$gte: X, $lt: Y}} 这种范围条件,mongos 能算出涉及哪些 chunk,定向到对应 shard,不用广播。这对时间范围、数值区间的查询非常友好。

范围分片的劣势是单调递增会热点。如果片键单调递增(时间戳、自增 ID),所有新数据的片键值都比现有的大,全落进「最大值」那个 chunk,而这个 chunk 通常在最后一个 shard。于是所有写入集中到一个 shard,形成严重热点——这个 shard 扛着全部写入,其他 shard 闲置,完全失去了分片的负载分散意义。

哈希分片的优势是写均匀分散。哈希把单调递增的值打散成随机分布,写入均匀打到各个 shard,没有热点。这对写入量大的场景(日志、事件流、IoT)非常关键。

哈希分片的劣势是范围查询无法定向。哈希后片键值失去了顺序性,原本相邻的值分散到不同 chunk。范围查询不知道涉及哪些 chunk,只能广播到所有 shard。所以哈希分片不适合有范围查询需求的场景。

怎么选

选择的核心是两个问题:业务有没有范围查询?片键会不会单调递增?

有范围查询需求(时间、数值区间)→ 范围分片。但要确保片键不是单调递增的,否则热点。如果片键本身就是递增的(比如时间),范围分片会热点,这时要么换片键,要么用下面的混合策略。

无范围查询、按 ID 等值查询 → 哈希分片。写入均匀是最大优势,ID 等值查询能定向(按哈希值定位 chunk)。日志、事件、按文档 ID 查的场景适合。

两者都要(既有范围查询又怕热点)→ 混合策略。几种做法:

  • 复合片键 {范围字段, 哈希字段}:比如 {userId, 时间} 范围分片,userId 分散写入、时间聚合同用户数据。或者对单调键加一个分散前缀。
  • 对单调递增键做哈希分片 + 辅助索引:片键用哈希保证写均匀,范围查询靠在非片键字段建索引(虽然查询会广播,但索引在各 shard 内高效)。
  • 权衡接受广播:如果范围查询不频繁,哈希分片 + 偶尔广播也能接受。

几个实际场景的推荐

日志/事件流(写入大、按 ID 查、偶尔按时间扫):哈希分片(按文档 ID 或机器 ID 哈希),写均匀是第一优先。

用户内容/订单(按用户维度访问):范围分片 {userId, 时间},用户查询定向、单用户数据聚集、写入按用户分散。

时间序列数据(按时间范围查、写入单调递增):最棘手的场景。纯时间范围分片会热点。可用 {标签字段, 时间} 复合片键(标签字段分散写入,时间保范围查询),或桶模式 + 哈希。

纯 ID 查询(KV 场景):哈希分片,简单高效。

判断框架

  • 范围分片保范围查询、牺牲写分散;哈希分片保写均匀、牺牲范围查询。
  • 高频范围查询 → 范围分片,但警惕单调递增热点。
  • 写入大、ID 等值查询 → 哈希分片。
  • 两者都要 → 复合片键或哈希+辅助索引。
  • 时间序列是最难的场景,用复合片键或桶模式。
  • 记住:范围查询多别用哈希;写入怕热点别用范围分单调键。

下一篇讲 chunk 和 balancer,看清数据怎么在 shard 间均衡。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。

我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。

如果你想继续跟完这套「图解 MongoDB」,欢迎关注公众号 「十三Tech」。后续会按分片集群和架构选型这条线更新。

十三Tech公众号二维码