为什么你的索引“没用上”?
在 MongoDB 中,创建索引 ≠ 查询变快。索引是否生效,取决于查询语句是否能“命中”索引结构。
❌ 常见误区
误区 | 说明 |
创建了单字段索引,但查询用 | 可能无法使用索引 |
复合索引顺序错误 | 如索引 |
查询中包含 | 可能导致索引失效 |
数据类型不匹配 | 如字段是 |
核心工具:explain()
执行计划分析
explain()
是 MongoDB 的“SQL 执行计划”工具,用于查看查询的执行细节。
基本语法
db.collection.find({ ... }).explain("executionStats")
关键输出字段
字段 | 说明 |
| 是否执行成功 |
| 返回文档数 |
| 扫描的文档总数(越小越好) |
| 扫描的索引条目数(越小越好) |
| 查询总耗时(毫秒) |
| 使用的索引名称 |
核心判断标准:
- ✅
totalDocsExamined ≈ nReturned
→ 索引高效 - ⚠️
totalDocsExamined >> nReturned
→ 索引未命中,全表扫描!
实战:从慢查询到索引优化
场景:用户订单查询慢
// 查询:查找用户 "alice" 在 2024 年的订单
db.orders.find({username: "alice",order_date: { $gte: "2024-01-01", $lt: "2025-01-01" }
})
步骤 1:执行 explain
db.orders.find({username: "alice",order_date: { $gte: "2024-01-01", $lt: "2025-01-01" }
}).explain("executionStats")
步骤 2:分析结果
"executionStats": {"executionSuccess": true,"nReturned": 150,"totalDocsExamined": 1200000, // 扫描了 120 万文档!"totalKeysExamined": 0, // 索引条目为 0 → 未使用索引"executionTimeMillis": 1250
}
📌 结论:全表扫描,性能极差!
步骤 3:创建复合索引
// 创建复合索引:先按 username,再按 order_date 排序
db.orders.createIndex({ username: 1, order_date: 1 })
步骤 4:再次执行 explain
"executionStats": {"nReturned": 150,"totalDocsExamined": 150, // 扫描文档数 = 返回数 ✅"totalKeysExamined": 150, // 索引命中 ✅"executionTimeMillis": 15 // 耗时从 1250ms → 15ms!
}
📌 性能提升 80 倍!