分片阶段的最后一篇,讲查询怎么在分片集群里路由。这是分片性能优化的核心——同样一个查询,带片键和不带片键,代价可能差 N 倍(N 是 shard 数)。理解查询路由,才能理解为什么分片集群里「查询要带片键」是第一原则。

先把机制边界说清楚

分片集群里,应用把查询发给 mongos,mongos 决定查询发到哪些 shard。路由方式有两种:

  • 定向查询(Targeted Query):查询条件带片键。mongos 根据片键值查 config server,定位数据在哪个 shard,只把查询转发到那个 shard。
  • 广播查询(Scatter-Gather / Broadcast Query):查询条件不带片键。mongos 不知道数据在哪个 shard,只能把查询发到所有 shard,各 shard 分别查,mongos 汇总结果。

两者的代价差别是数量级的:定向查询只动一个 shard,广播查询动全部 shard。shard 越多,广播的放大效应越大。

定向查询 vs 广播查询

查询路由:定向 vs 广播

定向查询是分片集群的理想状态。查询带片键,mongos 精准路由到单个 shard,那个 shard 用自己的索引高效查询,返回结果。整个过程中,其他 shard 完全不参与,负载不放大。这就像在图书馆里,知道书在哪个书架(片键),直接去那个书架找。

广播查询是分片集群的性能隐患。查询不带片键,mongos 不得不把查询发到所有 shard,每个 shard 都要查一遍。这就像不知道书在哪个书架,只能把所有书架都翻一遍。shard 越多,翻的次数越多,负载放大 N 倍。

什么查询能定向

能定向的查询条件:

  • 片键等值查询{userId: 12345}。mongos 精确定位到含这个片键值的 chunk 所在 shard。
  • 片键前缀查询:复合片键 {userId, date} 下,{userId: 12345}{userId: 12345, date: ...} 能定向(用了前缀)。
  • 片键范围查询(范围分片下){createdAt: {$gte: X, $lt: Y}} 在范围分片下,mongos 能算出涉及哪些连续 chunk,定向到对应 shard(可能多个,但不是全部)。哈希分片下范围查询无法定向。

不能定向、会广播的查询:

  • 不含片键的任何查询。
  • 复合片键但跳过了前导字段的查询(比如 {date: ...} 没带 userId)。
  • 哈希分片下的范围查询。

广播查询的代价与应对

广播查询不是绝对不能用,但要清楚它的代价:

负载放大:N 个 shard,广播查询让每个 shard 都执行一次。原本一个 shard 能完成的查询,变成 N 个 shard 都做一遍。

结果合并:mongos 要汇总所有 shard 的结果。排序、分页、聚合在广播下更复杂——mongos 要从各 shard 收集部分结果,再全局合并。

应对广播查询的策略:

  • 尽量让查询带片键。这是根本解。设计查询时,把片键作为查询条件的一部分。
  • 必须广播时,加 limit 和索引。limit 限制每个 shard 的返回量,索引减少各 shard 内的扫描量,降低单 shard 的广播代价。
  • 聚合用部分聚合。聚合管道里,能先在各 shard 内做的阶段(match、group)先做,只把部分结果传给 mongos 合并,比全量拉回高效。
  • 评估是否能用定向替代。有些查询换个写法或加个片键条件就能定向。

跨 shard 操作的限制

分片集群有些操作比单机/复制集受限:

跨 shard 事务:4.2+ 支持分片事务,但有性能代价——涉及多个 shard 的事务要跨 shard 协调,比单 shard 事务慢。核心交易尽量设计成单 shard 事务(让相关数据在同一 shard)。

排序和分页:广播查询的排序,mongos 要从各 shard 收集结果再全局排序。深度分页(skip 大值)在广播下代价极高。

唯一索引约束:分片集合的唯一索引必须包含片键前缀(否则无法保证跨 shard 唯一性)。这是分片的一个设计约束。

判断框架

  • 带片键 = 定向(高效);不带片键 = 广播(昂贵)。
  • 高频查询必须带片键前缀,这是分片性能第一原则。
  • 范围分片下片键范围查询可定向;哈希分片下范围查询必广播。
  • 必须广播时:limit + 索引 + 部分聚合,降低代价。
  • 跨 shard 事务和深度分页代价高,尽量设计成单 shard。
  • 分片集群的查询优化,核心就是「让查询带片键」。

这一篇是分片集群阶段的收尾。下一阶段进入运维与架构,讲备份恢复、Change Stream、监控、安全和选型。


关于十三Tech

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

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

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

十三Tech公众号二维码