MongoDB 7.0 中分片查询下推自动触发,仅当 $match 条件被分片键前缀完全覆盖且使用支持操作符(如 $eq、$in、$gt/$lt 等)时生效;哈希分片键仅支持 $eq/$in,复合键须匹配前缀字段;未覆盖则广播查询,可通过 explain("executionStats") 中各 shard 的 stage 和 docsExamined 确认是否真正下推。

分片查询下推在 MongoDB 7.0 中不是“开关式优化”,而是自动触发的路由层行为——只要查询条件能被分片键前缀覆盖,$match 就会下推到各 shard 执行,无需额外配置。
哪些查询条件能触发下推?
下推生效的前提是:查询中的 $match 阶段能被分片键(或其前缀)**完全覆盖**,且操作符在支持范围内。不满足时,mongos 会退为广播查询。
- 支持下推的操作符:
$eq、$in(单值或小数组)、$gt/$lt等范围操作(仅对范围分片键有效),以及复合键中前缀字段的等值匹配 - 不支持下推的典型写法:
$regex(无索引支持时)、$text、$where、$expr中引用非分片键字段、含未索引字段的$and组合 - 哈希分片键只支持
$eq和$in下推;$gt对{user_id: "hashed"}无效,会广播到所有 shard - 复合分片键如
{region: 1, user_id: "hashed"},只有{region: "cn"}或{region: "cn", user_id: ObjectId(...)}能下推;单独{user_id: ...}不能
怎么确认下推是否真的发生了?
别信 explain 里 “stage: SHARD_MERGE” 的表象,重点看执行路径是否真正下沉。最直接的方式是查 explain("executionStats") 输出中的 executionStages.shards 字段。
- 每个 shard 条目里若出现
"stage": "IXSCAN"或"stage": "COLLSCAN",说明该 shard 独立执行了过滤,即已下推 - 若所有 shard 的
docsExamined值都接近全量文档数(比如集合有 100 万,每个 shard 返回 docsExamined ≈ 20 万),基本是广播了 - 注意
executionStages.nReturned总和应等于最终结果数;如果远小于docsExamined总和,说明大量扫描被 mongos 合并时丢弃——这是下推失效的典型信号
为什么加了索引还是没下推?
索引存在 ≠ 查询能下推。关键卡点常在分片键设计与查询模式错位,而非索引缺失。
- 分片键是
{tenant_id: 1, created_at: -1},但查询只带{created_at: {"$gt": ...}}→ 前缀缺失,无法路由,必然广播 - 建了
{tenant_id: 1}索引,但分片键是{tenant_id: "hashed"}→ mongos 不会用该索引做路由判断 - 查询用了
$or,其中一条分支不含分片键前缀 → 整个$or表达式无法下推(MongoDB 不拆解 $or 做部分下推) - 分片键字段值为
null或缺失,且查询条件又没排除该情况 → 这些文档会被路由到一个默认分片,但其余分片仍需响应,造成隐性广播
聚合管道里下推有哪些限制?
下推只发生在管道最前端的 $match,且仅限于能被分片键覆盖的部分。后续阶段一律在 mongos 层合并后执行。
-
[{$match: {region: "us"}}, {$group: {_id: "$status", cnt: {$sum: 1}}}]→$match下推,$group在 mongos 归并 -
[{$match: {region: "us", score: {$gt: 80}}}, {$lookup: {...}}]→ 只要score不是分片键一部分,整个$match可能无法下推(取决于是否能被前缀覆盖) - 即使
$lookup关联集合有索引,它本身不会下推——关联动作总是在 mongos 层发起,只是被关联端能走索引加速 - 7.0 的
$mergeCursors优化的是 mongos 合并效率,不是下推能力;它让各 shard 并行返回结果,但不改变“谁来执行过滤”这一根本逻辑
最容易被忽略的是:下推效果高度依赖查询语句结构与分片键定义的严格对齐。一次看似微小的字段顺序调整(比如把 {a: 1, b: 1} 键写成 {b: 1, a: 1} 查询),就可能导致从精准路由退化为全量广播。验证必须落到每个 shard 的实际 docsExamined 数字上,而不是只看 explain 有没有 “SHARD” 字样。


















