绝大多数Scatter-Gather查询因缺失分片键完整前缀或使用mongos无法推导路由的操作符(如$ne、$not、非^开头$regex等)导致广播;确认方式是通过explain检查nShards>1且各shard executionTimeMillisEstimate均非零。

绝大多数 Scatter-Gather 查询不是配置错误,而是查询条件没带分片键完整前缀,或者用了 mongos 无法推导路由的操作符。
为什么你的查询悄悄广播了
mongos 只有在能**唯一确定目标分片**时,才走 targeted query;否则默认发往所有分片。常见误判点:
- 查
{userId: "u123"},但分片键是{region: 1, userId: 1}—— 缺少region,没法定位分片 - 用
$ne、$not、$regex(非^prefix形式)、$where—— 这些操作符不支持路由推导 - 聚合里
$match被包在$or里,或放在$lookup后面 —— mongos 放弃路由判断 - 分片键字段类型不一致:比如
region有的存"cn"(string),有的存ObjectId("...")—— 哈希值错位,路由表查不到对应 chunk
怎么确认是不是 Scatter-Gather
别看 mongos 日志总耗时,要直接看执行计划里的分片级指标:
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
- 运行
db.collection.explain("executionStats").find({ ... }) - 检查返回中的
nShards:如果 > 1,且你本意是单分片,基本就是广播了 - 展开
executionStages.shards,看每个分片的executionTimeMillisEstimate是否都非零 —— 全非零 = 真广播 - 对聚合,必须确保最外层
$match包含分片键完整前缀,且不在$or内部
哪些写法能稳住 Targeted Query
只要满足下面任意一条,mongos 就能精准路由:
- 普通查询:条件中包含分片键的**连续前缀字段**,比如分片键是
{a: 1, b: 1, c: 1},那么{a: 1}、{a: 1, b: 2}、{a: 1, b: 2, c: 3}都行,但{b: 2}或{a: 1, c: 3}不行 - 聚合管道:第一阶段必须是
$match,且该$match条件满足上述前缀规则;后续阶段不能破坏路由上下文(比如加$lookup到非分片集合) - 范围查询:只对分片键前缀做范围(
$gt/$lt)安全,但若分片键是哈希过的(如{_id: "hashed"}),任何范围查询都会退化为全分片扫描
容易被忽略的硬伤:分片键本身就不适合查询模式
即使语法全对,如果分片键和业务查询严重错配,Targeted Query 也救不了性能:
- 高频查询总是带
{createdAt: {$gt: ISODate(...)}},但分片键是哈希_id—— 每次都得扫所有分片 - 分片键基数太低(比如只有 5 个
region值),导致 chunk 分布不均,部分分片扛了 80% 的查询流量 - 写入集中在某个前缀(如
region: "us"),新文档全打到同一组 shard,形成热分片,查询延迟毛刺明显
这时候调索引、改查询写法都没用,真正要动的是分片键设计或重新分片。

















