单个分片高CPU不一定是分片本身问题,需先确认是否为主节点独扛写入与查询;再排查未带分片键的广播查询、chunk分布倾斜、WiredTiger缓存不足或压缩配置失配等根因。

确认是不是真的“单个分片”在扛压
直接看监控图表说“shard0 CPU 95%”容易误判——它可能只是副本集里的主节点(PRIMARY)在干活,而同属该分片的从节点(SECONDARY)CPU 很低。MongoDB 分片集群里,每个 shard 实际是一个副本集,写入和大部分读请求默认落到主节点上。先别急着调参数,用 rs.status() 登录到该分片任意节点,确认当前主节点是哪台:
- 如果主节点固定在某一台(比如
shard0-primary:27017),那高 CPU 就是它在处理所有写+路由过来的查询 - 如果主节点频繁切换(
stateStr反复变PRIMARY/SECONDARY),说明选举不稳定,可能是网络抖动或 oplog 覆盖导致从节点同步失败,进而引发持续重同步 - 检查
members[n].lastHeartbeatRecv时间戳是否延迟 >2 秒,这是心跳异常的明确信号
查慢查询和未命中分片键的广播操作
分片集群里最典型的 CPU 火山口,就是没带分片键的查询。mongos 收到这类请求后,会把查询广播到所有分片,每个分片都得全量扫描自己那部分数据,再把结果汇总。对单个分片来说,它并不知道自己只是“被拉壮丁”,CPU 就这么被干爆了。
- 登录该分片的主节点,执行
db.currentOp({ "secs_running": { "$gt": 5 } }),重点看secs_running高、ns是你业务集合、且query里不含分片键字段的操作 - 用
db.setProfilingLevel(1, { slowms: 100 })开启慢日志(生产环境慎用,建议只开几分钟),然后查system.profile,过滤millis > 500的记录,看有没有大量type: "query"但shardFilter字段为空的条目 - 典型错误写法示例:
db.orders.find({ status: "shipped" })—— 如果分片键是{ userId: 1, orderId: 1 },这个查询就必然广播
检查 chunk 分布是否严重倾斜
即使所有查询都带分片键,如果 chunk 在分片间分布不均,也会让某一个分片承担远超平均值的数据量和请求量。WiredTiger 缓存压力、索引加载、磁盘 IO 都会集中爆发在热点分片上。
- 连上
mongos,运行sh.status(),重点关注chunks列:比如shard0有 1280 个 chunk,shard1只有 80 个,那基本可以断定是数据分布问题 - 观察
jumbo chunk是否存在(sh.status()输出里带jumbo: true的 chunk)。这类 chunk 因为过大(>~128MB 或文档数超限)无法被 balancer 拆分或迁移,会长期卡在某个分片上 - 手动触发均衡:
sh.startBalancer()后等 10 分钟,再sh.status()看 chunk 是否开始流动;如果不动,可能是balancer被禁用了,或者目标分片磁盘空间不足(db.stats().dataSize / db.stats().storageSize> 0.9)
警惕 WiredTiger 缓存与压缩配置失配
CPU 高但磁盘 IO 不高?很可能是 WiredTiger 在后台疯狂做压缩/解压。默认配置下,如果集合用了 zstd 压缩(MongoDB 6.0+ 默认),而服务器 CPU 核心少、主频低,压缩过程就会吃满 CPU。
- 查该分片的存储引擎配置:
db.serverStatus().wiredTiger["cache"]["maximum bytes configured"],对比物理内存。如果设得太小(比如只配了 4GB,但机器有 64GB),缓存命中率暴跌,频繁从磁盘读取 + 解压,CPU 就上去了 - 查压缩方式:
db.runCommand({ collStats: "your_collection" }).wiredTiger["block-manager"]["compression"],如果是zstd且 CPU 弱,可考虑重建集合改用snappy:db.your_collection.createIndex({ ... }, { storageEngine: { wiredTiger: { configString: "block_compressor=snappy" } } }) - 注意:修改压缩方式需重建集合,务必在低峰期操作,并提前备份
真正难排查的,往往是多个因素叠加:比如 chunk 倾斜导致某分片数据多,又碰上一批没带分片键的定时任务查询,再加上 WiredTiger 缓存配置偏小,三股压力同时压在一台 mongod 进程上。这时候光看 CPU 指标没用,得把 currentOp、sh.status()、serverStatus 三组输出对照着看,才能揪出根因。











