分片集群里所有决策,没有比**片键(shard key)**更重要的了。片键决定了数据怎么分布到各个 shard、写入会不会形成热点、查询能不能定向到单个 shard。更关键的是,片键一旦选定极难更改(虽然新版有 reshardCollection,但仍是大工程)。片键选错,整个集群会数据倾斜、写入热点、查询全广播,且很难回头。

这一篇是分片阶段的核心,讲清楚选片键要同时满足的三个目标,以及常见片键各自的取舍。

先把机制边界说清楚

片键是分片集合里用来决定「这条数据存到哪个 shard」的字段(或字段组合)。MongoDB 按片键的值,把数据划分成一个个 chunk(块),每个 chunk 是一段连续的片键范围,分配到某个 shard。balancer 会在 shard 间迁移 chunk 保持均衡。

所以片键直接决定了:

  • 数据分布:片键值越分散,数据越均匀;片键值越集中,数据越倾斜。
  • 写入热点:片键单调递增时,新数据总落在「最大值」那个 chunk(通常在最后一个 shard),形成热点。
  • 查询路由:查询带片键前缀时,mongos 能定向到单个 shard;不带片键则广播到所有 shard。

片键的三个目标,就是从这三个维度提出的。

片键的三个目标

片键设计:决定集群命运的一个决定

① 基数高(Cardinality)。片键的取值要足够多样。如果片键只有几个值(比如 status 只有 active/inactive),数据无法有效分散,会集中到少数 chunk 和 shard。基数越高,越能细粒度分散。ObjectId、userId 这类高基数字段合格;status、type 这类低基数字段绝对不能用。

② 写分散(Write Distribution)。写入要均匀打到各个 shard。如果片键是单调递增的(时间戳、自增 ID、ObjectId 的范围分片),所有新数据都落在最大值那个 chunk,写入集中到一个 shard,形成热点——这个 shard 扛着全部写入压力,其他 shard 闲置。哈希分片是解决单调递增热点的常用手段(下一篇展开)。

③ 查询定向(Targeted Query)。高频查询要能带上片键,这样 mongos 能定向到单个 shard。如果高频查询不带片键,每次都广播到所有 shard(scatter-gather),放大负载。所以片键应该是业务高频查询会带上的字段。

这三个目标往往互相冲突:基数高且写分散的键(如随机 ID)通常查询难定向;查询能定向的键(如时间范围)往往单调递增会热点。选片键就是在这三者间找平衡。

常见片键的取舍

把常见片键按三个目标评估:

时间戳/自增 ID(范围分片)。基数高,范围查询能定向,但写入全部集中到最后一个 shard,形成严重热点。除非配合别的字段,否则不适合单独做片键。

ObjectId 哈希分片。基数高、写均匀分散(哈希打散了单调性),是最通用的选择。代价是查询很难定向(按 ObjectId 查的场景少),多走广播。适合「没有明显查询定向需求、只求分散」的场景。

userId 范围分片。基数高(用户多)、按用户查询能定向、写较分散(不同用户的数据分散)。适合以用户为维度的业务(用户内容、用户订单)。但单个大用户的数据会集中在一个 chunk,形成局部热点。

复合片键 {userId, 时间}(范围分片)。这是常被推荐的组合:userId 保证查询定向和分散,时间作为第二字段让单用户的数据按时间聚集(方便按时间范围查)。兼顾了三个目标,适合多数用户维度业务。

粗粒度键(如 status、type)。基数极低,数据完全无法分散,会形成 jumbo chunk(无法迁移的超大块)。绝对不能用作片键。

怎么选:按业务查询模式出发

选片键的正确姿势是从业务的高频查询反推,而不是从字段挑:

  1. 列出业务最高频的查询(比如「按 userId 查用户订单」「按时间范围查日志」)。
  2. 找出这些查询都会带上的字段——它就是片键的候选。
  3. 评估这个字段的基数(够不够分散)和写入模式(会不会热点)。
  4. 如果单调递增会热点,考虑加哈希或复合字段。
  5. 如果单字段无法兼顾,用复合片键。

核心判断:片键应该是业务查询的「天然维度」。用户维度业务用 userId,日志业务用时间(配合哈希或机器 ID 防热点),内容业务用内容 ID。片键和查询模式对齐,查询才能定向,集群才能高效。

片键的不可逆性

强调一遍:片键极难更改。老版本改片键几乎不可能(要重建集合);新版有 reshardCollection,但要重新分布所有数据,是个耗时且高风险的大操作。所以片键必须在上线前仔细设计,一旦数据量大了,错了就是长期的痛。

评估片键时,要模拟未来的数据增长和访问模式:一年后数据量翻十倍,这个片键还撑得住吗?查询模式会变吗?宁可上线前多花时间设计,也不要带着错误的片键上线。

判断框架

  • 片键三个目标:基数高、写分散、查询定向,三者难兼顾要取舍。
  • 从高频查询反推片键,而不是随便挑字段。
  • 单调递增键(时间戳/自增)单独用会写热点,配哈希或复合字段。
  • 低基数键(status/type)绝对不能用,会形成 jumbo chunk。
  • 复合片键 {粗维度, 细维度} 是兼顾三者的常用形态。
  • 片键几乎改不了,上线前模拟未来增长仔细设计。

下一篇讲范围分片 vs 哈希分片的取舍。


关于十三Tech

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

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

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

十三Tech公众号二维码