什么是复合索引(Compound Index)?

复合索引是指在多个字段上创建的单个索引,用于加速涉及多个字段的查询条件或排序操作。

// 示例:在 status 和 createdAt 字段上创建复合索引
db.orders.createIndex({ status: 1, createdAt: -1 })

✅ 适用场景:

场景

示例查询

多条件查询

db.orders.find({ status: "paid", type: "online" })

查询 + 排序

find({ status: "paid" }).sort({ 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 排序

字段顺序应遵循以下优先级:

  1. 等值条件字段(Equality)→ 放最前面
  2. 排序字段(Sort)→ 放中间
  3. 范围查询字段(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 和内存消耗。记住:索引不是越多越好,而是越精准越好。