聚合管道中只有最前端的$match能走集合索引,后续阶段均不走索引;验证需用explain("executionStats")检查winningPlan.stage是否为IXSCAN、indexName是否匹配及totalKeysExamined是否接近nReturned。

聚合管道里 $match 阶段是否走了索引?
聚合管道中只有 $match(且位于最前端)能利用集合层面的索引;后续阶段如 $unwind、$sort、$group 都不走索引,它们操作的是内存中的中间结果集。所以关键就看第一个 $match 是否命中了底层集合的索引。
验证方法是用 explain("executionStats") 包裹整个聚合命令:
db.collection.aggregate([
{ $match: { status: "active", createdAt: { $gte: ISODate("2026-07-01") } } },
{ $sort: { score: -1 } }
]).explain("executionStats")
重点检查返回结果里的:
-
winningPlan.stage是IXSCAN而不是COLLSCAN -
indexName字段存在且值是你预期的索引名(比如"status_1_createdAt_1") -
totalKeysExamined接近nReturned,说明索引过滤效率高;如果totalKeysExamined远大于nReturned,可能是索引前缀没对齐或范围查询太宽
为什么 explain() 显示 IXSCAN,但 $indexStats 里 accesses.ops 为 0?
这是典型误判场景:explain() 只反映单次执行路径,而 $indexStats 统计的是真实历史访问次数。聚合管道中若 $match 出现在 $unwind 或 $lookup 之后,它扫描的是中间文档,不是原始集合——这种“扫描”不会触发 accesses.ops 计数。
也就是说:
-
db.collection.aggregate([{ $unwind: "$tags" }, { $match: { "tags": "hot" } }]):这个$match不走集合索引,$indexStats不会增长 -
db.collection.aggregate([{ $match: { status: "hot" } }, { $unwind: "$tags" }]):这个$match才真正走索引,$indexStats会更新
别只信 explain() 的 stage 名称,得结合位置和语义判断它到底扫谁。
聚合中 $sort 没走索引,怎么确认是不是瓶颈?
$sort 阶段永远不会使用 B-tree 索引做排序(MongoDB 不支持索引覆盖排序输出),但它可以利用索引完成“内存外排序”的规避:如果 $sort 字段和前面 $match 条件一起构成了一个已建索引的前缀,MongoDB 会按索引顺序读取数据,自然有序,从而跳过内存排序。
判断依据看 explain() 输出:
- 有
sortStage字段且stage是SORT→ 真正做了内存排序,危险(尤其当memUsage> 100MB 或出现spilled) - 没有
sortStage,且winningPlan.stage是IXSCAN→ 数据按索引物理顺序读入,已天然有序 - 索引必须包含所有
$sort字段,且顺序一致,且在$match使用的字段之后(例如索引{a: 1, b: 1, c: -1}支持$match {a: 1} + $sort {b: 1, c: -1})
生产环境如何持续监控聚合索引使用率?
单靠人工 explain() 完全不可靠。必须结合两个手段交叉验证:
- 开启慢查询日志:
db.setProfilingLevel(1, { slowms: 100 }),然后查db.system.profile.find({ "queryPlanner.winningPlan.stage": "IXSCAN", "millis": { $gt: 50 } }),确认哪些聚合语句真正在业务高峰用了索引 - 定期跑
$indexStats并比对:db.collection.aggregate([ { $indexStats: {} } ]),重点关注accesses.ops长期为 0 的索引——特别是你“以为”聚合在用的复合索引 - 注意
accesses.since时间戳,如果 mongod 刚重启过,ops === 0不代表没用,要等几小时再观察
最容易被忽略的一点:聚合里用 $lookup 关联另一个集合时,那个被关联集合上的索引是否建对了?它的 $indexStats 是独立统计的,别只盯着主集合看。

















