一眼看出查询被广播:执行explain("executionStats"),若shards字段列出多个shard且各shard的totalDocsExamined高而nReturned小,即为Scatter-Gather广播行为。

怎么一眼看出查询被广播了
直接跑 explain("executionStats"),重点看返回结果里的 shards 字段。如果它下面列出了多个 Shard(比如 shard0000、shard0001),且每个 Shard 的 totalDocsExamined 都很高,但 nReturned 很小——基本就是 Scatter-Gather(广播)行为。
典型错误现象:
-
find({status: "active"})这种没带分片键的查询,在分片集群上默认就广播 - 前端漏传分片键字段(如查订单却没传
orderId或userId) - 聚合管道里
$lookup后丢失了分片键上下文,导致后续$match无法路由
为什么 sh.status() 和 getShardDistribution() 必须先跑
别急着改查询或加索引,先确认“慢”是不是假象:数据根本没真正分片。
运行 sh.status() 看三件事:
- chunks 是否已分配(不是全在
primaryshard 上) - balancer 是否开启(
balancer is running: true) - 各 Shard 的文档数是否严重倾斜(比如一个占 95%,其余为 0)
再跑 db.collection.getShardDistribution()(把 collection 换成你的集合名):
- 只返回 1 行?说明压根没分片成功
- 某 Shard 的
size是其他几十倍?大概率是分片键基数太低(比如用status分片)
注意:getShardDistribution() 读的是 config server 缓存,刚写入的数据可能还没触发 chunk 拆分,别立刻下结论。
C# Driver 不报错,但会静默走广播路径
官方 C# Driver 的 Find() 方法遇到无分片键查询时,不会抛异常,也不会警告——它就老老实实把请求发给所有 Shard,等 mongos 合并结果再返回你。
这意味着你看到的耗时 = 所有 Shard 响应时间 + mongos 合并开销,而不是单个 Shard 的延迟。
排查建议:
- 在代码里对关键查询加日志,打印实际发送的 filter 内容,确认是否含分片键字段
- 用 MongoDB Compass 或 mongosh 跑同样 filter 的
explain("executionStats")对比验证 - 避免在业务逻辑里拼接动态查询条件时,遗漏分片键字段(尤其多条件组合场景)
explain 中 totalDocsExamined 高 ≠ 一定是广播,但一定有问题
totalDocsExamined 显著高于 nReturned,说明 MongoDB 在索引扫描后仍需大量回表过滤——这可能是:
- 查询带了分片键,但没建对应索引(比如分片键是
{userId: 1, createdAt: -1},却只建了{createdAt: 1}) - 复合索引字段顺序和查询条件不一致(
find({createdAt: {$gt: ...}, userId: "abc"})匹配不了{userId: 1, createdAt: -1}) - 用了表达式或函数(如
{$substr: [...]}),导致索引失效
真正要区分的是:shards 字段是否多条记录。有,就是广播;只有 1 条但 totalDocsExamined 高,那是索引没建对或没覆盖全。


















