$indexStats 对聚合耗时分析无效,因为它只统计索引累计访问次数等静态指标,不记录时间戳、不绑定具体聚合调用、不采集CPU/IO耗时,无法定位$sort磁盘排序或$lookup缺索引等阶段级性能问题。

MongoDB聚合管道本身不暴露各阶段的执行耗时,$indexStats 也不能直接用于分析聚合阶段性能——它只返回索引使用统计(如访问次数、内存用量),和管道执行过程无关。
为什么 $indexStats 对聚合耗时分析无效
$indexStats 是一个集合级命令,输出的是每个索引被查询引擎调用的累计指标(如 accesses.ops),不记录时间戳、不区分操作类型、也不绑定到某次 aggregate() 调用。它无法告诉你 $sort 阶段卡在磁盘排序,或 $lookup 因缺少索引慢了 200ms。
- 它不采集 CPU/IO 时间,只统计“被用了多少次”
- 它不关联具体聚合语句,多个并发聚合共享同一组统计值
- 即使索引被高频访问,也不能反推当前聚合慢是因为索引问题,还是数据膨胀、内存不足或阶段顺序不当
真正能定位聚合阶段耗时的方法
用 explain("executionStats") 获取单次聚合的完整执行快照,重点关注 executionStages 中每个阶段的 executionTimeMillisEstimate 和 totalDocsExamined:
-
$match阶段耗时高 → 检查是否命中索引(看indexName字段是否存在,isMultiKey是否为 true) -
$sort阶段memUsage超过 100MB 或出现spills→ 必须加allowDiskUse: true,且应前置$match减少待排序文档量 -
$group的executionTimeMillisEstimate突增 → 检查分组键基数是否过高(如按$uuid分组),或未投影精简字段导致内存暴涨 -
$lookup耗时占比大 → 确认被关联集合的localField/foreignField是否有支撑索引(需建在目标集合上,不是源集合)
容易被忽略的性能陷阱
聚合慢常常不是某个阶段“写错了”,而是多个小决策叠加的结果:
- 在
$project里保留了 20 个不用的字段,让后续所有阶段都多搬 80% 数据 - 把
$sort放在$match后面没问题,但放在$unwind后面就可能让排序文档数从 1k 暴涨到 500k - 用
$dateToString在$group键里生成小时字符串,比用$dateTrunc(5.0+)慢 3–5 倍,且无法利用日期索引 -
allowDiskUse: true开了,但没配maxTimeMS,导致慢聚合长期占着连接不释放
最准的耗时数据永远来自单次 explain("executionStats"),而不是靠 $indexStats 猜;而真正的优化点,往往藏在阶段顺序、字段投影和索引覆盖的交叉处。











