MongoDB 7.0 复合索引在聚合中仍严格遵循前缀匹配规则,且优化器不再重排 $match 条件适配索引顺序,字段顺序、类型一致性及路径精度必须严丝合缝。

MongoDB 7.0 的复合索引在聚合中仍遵循“前缀匹配”规则,但优化器对 $match 阶段的自动前置和字段依赖判断更严格——这意味着旧版本可能“侥幸走索引”的查询,在 7.0 下更容易失效,必须显式按索引顺序组织条件。
复合索引字段顺序在聚合中是否还重要
非常重要,且比 4.4/5.0 更敏感。MongoDB 7.0 的聚合优化器不会为 $match 阶段“重排”查询条件来适配索引顺序。例如你建了索引 { user_id: 1, order_date: 1, status: 1 }:
- 这个
$match能用上索引:{ user_id: ObjectId("..."), order_date: { $gte: ... } } - 这个
$match无法使用该索引(哪怕字段全在):{ order_date: { $gte: ... }, status: 1 }—— 缺少最左字段user_id,且order_date不是前缀 - 即使写成
{ status: 1, user_id: ... },也不会被重排序去匹配索引,7.0 明确放弃这类启发式尝试
为什么 $match 放在聚合开头也不一定走索引
表面看 $match 在第一阶段,但实际能否命中索引,取决于它是否满足索引前缀 + 查询字段存在性 + 类型一致性。7.0 加强了对隐式类型转换的拦截:
- 如果字段定义为
String,但查询传了Number(如{ "age": 25 }查String字段),7.0 直接跳过索引,而旧版本可能隐式转后扫描 -
$in中混合类型([123, "123"])会彻底禁用该字段的索引路径 - 使用
$regex且非前缀匹配(如/abc$/)时,7.0 的执行计划明确标记"indexBounds": [],表示未进入索引扫描
如何验证聚合是否真正覆盖索引(7.0 新要点)
别只看 explain("queryPlanner") 里有没有 "stage": "IXSCAN",要重点检查 executionStats 和索引键映射:
- 运行带
explain("executionStats")的聚合,确认docsExamined≈nReturned;若前者远大于后者,说明索引没滤掉数据,只是被“扫描”而非“定位” - 检查
executionStats.executionStages.inputStage.indexName是否是你期望的复合索引名 - 注意
executionStats.executionStages.inputStage.keysExamined:如果它远小于docsExamined,说明索引有效;若两者接近,说明几乎全索引扫描,等价于全表扫 - 7.0 新增
indexFilterSet字段,若为true表示有强制索引策略干扰,需排查db.runCommand({planCacheSetFilter: ...})
容易被忽略的聚合索引陷阱
7.0 对“索引覆盖”的要求更苛刻,尤其在涉及嵌套、数组、通配符时:
-
$unwind后的$match无法复用原始文档字段的复合索引,必须为展开后的路径单独建索引(如"items.price": 1) - 复合索引
{ a: 1, "b.c": 1 }不能用于查询{ "b.d": 1 },即使b是同一对象——7.0 不再尝试字段共祖推断 - 使用
allowDiskUse: true时,若中间结果超出内存限制,索引在后续阶段(如$group)完全失效,此时$sort必须靠$group前的独立索引支撑
真正关键的不是“有没有索引”,而是“聚合每一步是否在索引定义的字段顺序、类型、路径精度上严丝合缝”。7.0 把模糊地带收窄了,代价是调试时得一行行核对 explain 输出里的 indexKeyPattern 和实际查询字段。


















