scatter-gather广播查询是分片集群下查询变慢的主因,表现为mongos将本应路由至单分片的请求发往所有分片,延迟由最慢分片决定;需用explain("executionstats")验证nshards>1及各分片executiontimemillisestimate非零来确认。

先看是不是 Scatter-Gather 广播查询
分片集群下查询变慢,八成是本该打到单个分片的请求,被 mongos 发到了所有分片——这就是 Scatter-Gather。它不报错,但延迟直接卡在最慢那个分片上,不是平均值。
别猜,用 explain("executionStats") 看真实路由行为:
-
nShards字段大于 1,且你本意只查某区域或某用户,基本坐实广播 - 检查
executionStages.shards列表里每个分片的executionTimeMillisEstimate:多个非零值 = 真被广播了 - 注意:
mongos日志里的总耗时含协调开销,不能反映单分片执行时间;必须看explain返回的分片级指标
查分片键使用是否合规
Targeted Query 失效的核心原因,就是查询条件无法唯一确定一个分片。常见踩坑点:
- 分片键是复合键
{region: 1, userId: 1},但只查{userId: "u123"}—— 缺少前缀region,mongos 推导不出目标分片 - 用了不支持路由的操作符:
$ne、$not、$regex(非前缀匹配)、$where,或者聚合里用了$lookup/$facet等阶段 - 分片键字段类型不一致:比如
region有的存"cn"(string),有的存ObjectId(""),哈希计算错位,路由直接失效
聚合查询特别容易踩坑
聚合比普通查询更敏感,稍不注意就广播。安全写法有硬性约束:
-
$match阶段必须放在管道最外层,且条件中包含分片键完整前缀(如{region: "cn", userId: "u123"}) - 不能被
$or包裹——$or会让 mongos 放弃路由推导 - 如果要用
$lookup,被联查的集合也得是分片集合,且localField/foreignField要能对齐分片逻辑,否则照样广播
示例安全写法:
db.orders.aggregate([
{ $match: { region: "cn", userId: "u123" } },
{ $group: { _id: "$status", count: { $sum: 1 } } }
])
确认慢因是否真在路由层
广播查询不是 bug,有些场景它本就该出现:全量统计 db.users.countDocuments({})、按非分片键字段查 {email: "a@b.com"}、跨区域拉取数据。这时候瓶颈不在路由逻辑,而在各分片响应时间差异和 mongos 合并开销。
真正危险的是:你以为查的是单分片,结果悄悄广播了。这种情况往往伴随分片键设计不合理(比如用 createdAt 当分片键,导致数据倾斜),或者应用层拼查询条件时漏了关键字段。
复杂点在于:Scatter-Gather 和索引缺失可能同时存在。先用 explain 确认路由是否正常,再进单个分片查 executionStats 看扫描文档数和索引使用情况——顺序错了,优化就跑偏。











