什么是复合索引(Compound Index)?
复合索引是指在多个字段上创建的单个索引,用于加速涉及多个字段的查询条件或排序操作。
// 示例:在 status 和 createdAt 字段上创建复合索引
db.orders.createIndex({ status: 1, createdAt: -1 })
✅ 适用场景:
场景 | 示例查询 |
多条件查询 |
|
查询 + 排序 |
|
覆盖查询 | 所需字段都在索引中,无需回表 |
复合索引的核心原则
1️⃣ 前缀匹配原则(Prefix Matching)
MongoDB 可以使用复合索引的任意前缀子集进行查询优化。
假设索引为:{ a: 1, b: 1, c: 1 }
✅ 可以命中的查询:
{ a: 1 }
{ a: 1, b: 1 }
{ a: 1, b: 1, c: 1 }
❌ 无法命中的查询:
{ b: 1 }
(缺少前缀 a){ c: 1 }
{ b: 1, c: 1 }
📌 设计建议:将最常用于查询过滤的字段放在前面。
等值查询 vs 范围查询 vs 排序
字段顺序应遵循以下优先级:
- 等值条件字段(Equality)→ 放最前面
- 排序字段(Sort)→ 放中间
- 范围查询字段(Range)→ 放最后
// 推荐顺序
db.users.createIndex({ status: 1, // 等值:status = "active"age: 1, // 范围:age > 18name: 1 // 排序:sort by name
})
📌 错误示例:{ age: 1, status: 1, name: 1 }
→ 范围字段在前,索引效率大打折扣!
实战演示:使用 explain()
分析索引命中
场景:订单查询系统
// 创建复合索引
db.orders.createIndex({ status: 1, // 等值查询createdAt: -1 // 排序
})// 查询:查找已支付订单,按时间倒序
db.orders.find({ status: "paid" }
).sort({ createdAt: -1 }).explain("executionStats")
explain()
关键输出解读:
"executionStats": {"totalDocsExamined": 0,"totalKeysExamined": 120,"executionStages": {"stage": "IXSCAN", // 索引扫描,命中索引"indexName": "status_1_createdAt_-1"}
}
📌 关键指标:
"stage": "IXSCAN"
→ 命中索引"totalDocsExamined": 0
→ 无需回表(若为覆盖查询)"totalKeysExamined"
→ 扫描的索引条目数,越小越好
覆盖查询(Covered Query):性能极致优化
当查询的字段和返回的字段全部包含在索引中时,MongoDB 无需访问文档本身,直接从索引返回结果,性能最佳。
// 创建覆盖索引
db.users.createIndex({ status: 1, name: 1, email: 1
})// 查询仅返回索引字段
db.users.find({ status: "active" },{ name: 1, email: 1, _id: 0 } // 不返回 _id
).explain("executionStats")
📌 输出中 "totalDocsExamined": 0
表示完全覆盖,性能最优!
复合索引是 MongoDB 性能优化的“核心武器”。合理设计索引顺序,不仅能提升查询速度 10 倍以上,还能显著降低 CPU 和内存消耗。记住:索引不是越多越好,而是越精准越好。