cpu飙升大概率是跨分片广播扫描所致,因查询未带分片键或使用无法下推的操作符,导致mongos向所有分片广播全表扫描请求;直连primary查profile可确认是否为广播模式。

分片集群里部分节点CPU高,大概率不是负载不均,而是这些节点正在被反复广播扫描——你查的那条查询没带分片键,mongos没法路由,只能把请求发给所有分片,每个分片都扫一遍全表。
怎么确认是跨分片广播导致的CPU飙升
别连mongos看currentOp,它只显示路由层视图,看不到真实执行。必须直连问题分片的Primary节点(不是mongos),再执行:
-
db.setProfilingLevel(1, { slowms: 50 })开启 profiling - 查
db.system.profile.find({ "docsExamined": { "$gt": 100000 }, "nreturned": { "$lt": 10 } }).sort({ "ts": -1 }).limit(5) - 重点看:
"stage": "COLLSCAN"、"ns"指向同一集合、"client"来源分散、时间密集 - 对其中一条记录跑
explain("executionStats"),如果"shards"数组长度等于你集群分片总数,且各分片的"docsExamined"值接近,基本就是广播式扫描
哪些查询写法会触发广播到所有分片
根本就一个原因:mongos无法根据条件定位到单个分片。常见写法包括:
- 完全不带分片键字段,比如分片键是
{order_id: 1},却查{user_email: "a@b.com"} - 用了
$ne、$not、$regex(非前缀匹配,如/abc$/)等无法下推的操作符 - 复合分片键
{region: 1, timestamp: -1},只查{timestamp: {$gt: ISODate("...")}},漏了region - 聚合管道开头没
$match过滤分片键,比如[$lookup, $group, $match],$match放最后
$in 不一定广播——如果值能映射到有限分片,mongos会优化;但如果混了跨分片范围的值(比如 order_id 跨多个哈希槽),仍可能广播。
为什么只有一部分Shard节点CPU高
看似“部分节点”高,其实是所有分片都在扫,但监控粒度或采样时机让你只看到其中几个峰值。更常见的情况是:某个分片恰好刚完成一次大扫描,缓存还没热,下一轮广播请求进来时,它要重新读盘+解压+遍历,CPU毛刺更明显;而其他分片可能刚刷完缓存页,响应快些,看起来“正常”。别被瞬时差异骗了,先验证是不是广播模式——只要 shards 数组长度等于分片总数,就说明所有分片都参与了这次无效扫描。
真正容易被忽略的是:这种问题往往不是单个慢查询,而是大量轻量请求因路由失效被广播,每个分片都执行一次 COLLSCAN,CPU 是线性叠加的,不是“扫一次”。修复必须从查询源头改,加索引没用,不带分片键的查询永远无法路由。











