99%的mongodb cpu飙高源于collscan全表扫描;需用db.currentop()筛出超3秒操作,再以explain("executionstats")确认stage为collscan且docsexamined远大于nreturned,最后按查询字段顺序创建升序索引并验证ixscan生效。

直接结论:99% 的 MongoDB CPU 飙高,根源是 COLLSCAN —— 也就是全表扫描。不解决它,加机器、调连接池、升配置都只是临时止痛。
怎么快速确认是不是 COLLSCAN 在作祟
别猜,直接看实时正在跑的慢操作和执行计划:
- 运行
db.currentOp({ "secs_running": { "$gt": 3 } }),筛出已执行超 3 秒的操作;重点关注query字段内容和secs_running - 对每个可疑查询,套上
explain("executionStats"),例如:db.users.find({ email: "a@b.com", status: "active" }).explain("executionStats") - 检查返回里的
executionStats.executionStages.stage:如果值是COLLSCAN,且docsExamined远大于nReturned(比如查 1 条却扫了 50 万文档),就是它了 - 注意:
indexName为空不是唯一判断依据——复合索引字段顺序错,也会导致“有索引但不用”,仍走COLLSCAN
怎么生成真正能用的 createIndex() 命令
索引不是越多越好,顺序错了等于没建:
- 提取
explain中实际参与过滤的字段,严格按它们在find()或aggregate()的$match阶段出现的**先后顺序**排列 - 等值查询字段(
{ status: "active" })放前面,范围查询字段({ created_at: { $gt: ... } })放后面 - 所有字段默认用
1(升序),除非业务明确需要降序排序,否则别加-1—— 多数场景下升序索引兼容性更好 - 示例:若查询是
db.logs.find({ app_id: "web", level: "error", ts: { $gte: ... } }),对应命令应为db.logs.createIndex({ app_id: 1, level: 1, ts: 1 }),不能把ts提前
上线前必须验证的三件事
索引建完不等于问题消失,很多团队在这一步翻车:
- 建完立刻再跑一次
explain("executionStats"),确认stage变成IXSCAN,且docsExamined接近nReturned - 观察
system.profile或云厂商慢日志里是否还有同类型COLLSCAN记录(开启 profiling:db.setProfilingLevel(1, { slowms: 100 })) - 在低峰期上线,避免建索引过程本身锁表或拖慢写入;副本集环境下,索引会自动同步到从节点,但耗时可能较长,需留意
db.currentOp()中是否有background: true的建索引任务卡住
最容易被忽略的是字段顺序和等值/范围混合场景下的索引失效——哪怕只差一个字段位置,MongoDB 就可能放弃使用整个索引。每次加索引前,务必拿真实查询语句做 explain 验证,而不是凭经验写。











