先用db.currentOp()筛选运行超2秒的聚合操作,再查system.profile定位未走索引的慢聚合,最后用explain("executionStats")分析执行计划并针对性建索引。

查 db.currentOp() 看实时长耗时聚合操作
先确认是不是聚合查询正在吃 CPU:连上 MongoDB Shell,执行 db.currentOp({ "secs_running": { "$gt": 2 } }),重点筛出运行超 2 秒的活跃操作。聚合类操作通常 op 字段为 command 或 aggregate,ns 是目标集合,query 里能看到 $pipeline 内容。如果 secs_running 持续增长、numYields 很低,说明它卡在计算或扫描阶段,不是 I/O 等待。
从 system.profile 找未走索引的聚合慢日志
确保 profiling 已开启(推荐设为 1):db.setProfilingLevel(1, { slowms: 50 })。之后查最近的聚合慢日志:db.system.profile.find({ "ns": /your_db\.your_collection/, "command.aggregate": { "$exists": true } }).sort({ "microsecs_running": -1 }).limit(5)。关键看三处:
-
executionStats.executionStages.stage是否为COLLSCAN(全表扫)或IXSCAN但docsExamined远大于nReturned -
executionStats.totalDocsExamined超过 10000 就值得警惕 -
executionStats.executionTimeMillis高且stage深度大(比如嵌套多层$lookup+$unwind)
用 explain("executionStats") 定位缺失字段
对慢聚合语句补上 .explain("executionStats"),例如:db.orders.aggregate([ { $match: { status: "shipped", createdAt: { $gte: ISODate("...") } } }, { $group: { _id: "$region", total: { $sum: "$amount" } } } ]).explain("executionStats")。重点关注:
根据 package.json 中的 engines 字段自动检测 Node.js 版本需求,未安装 nvm 时自动安装并切换至对应版本。支持 Windows、macOS、Linux。适用于 Node.js 项目开发、版本切换和环境配置。
- 根
stage是COLLSCAN→ 缺少覆盖$match条件的索引 - 根
stage是IXSCAN,但indexBounds显示只用了部分字段 → 复合索引顺序不匹配前缀(如查询用{ a: 1, b: 1 }却建了{ b: 1, a: 1 }) - 出现
inMemorySort: true →$sort没命中索引,数据量大时 CPU 暴增
建索引要匹配聚合实际路径,不是照抄 $match
聚合中真正影响性能的不只是 $match,还有 $sort、$group 的分组键、$lookup 的本地/外键。建索引前必须明确聚合 pipeline 的执行顺序:
- 如果
$match在最前且条件固定,优先建该字段的单字段或复合索引(按查询频率排序字段顺序) - 若含
$sort: { region: 1, createdAt: -1 },且结果集不大,可建{ region: 1, createdAt: -1 }索引避免内存排序 -
$lookup的from表,其被关联字段(如user_id)必须有索引,否则每次 join 都全表扫 - 不要盲目加
background: true—— 它延长建索引时间,且对高并发聚合场景可能引发锁竞争
真正容易被忽略的是:聚合中 $project 或 $addFields 生成的新字段无法被索引,所有过滤/排序必须基于原始字段或已建索引的字段。一旦 pipeline 里有计算逻辑前置,索引就失效了。

















