MongoDB何时不使用索引 ?
参考回答
在以下几种情况下,MongoDB 查询可能不会使用索引:
- 没有为查询字段创建索引:如果查询字段上没有索引,MongoDB 会执行全表扫描(
COLLSCAN
)。 - 查询条件未命中索引:查询条件未与索引字段匹配或未按照索引的顺序使用。
- 返回数据占比过高:如果查询结果占文档总数的很大比例,MongoDB 可能会放弃使用索引,改为全表扫描。
- 索引被禁用:使用
hint
明确指定不使用索引。 - 排序未优化:如果排序字段没有索引支持,MongoDB 会放弃索引并进行内存排序。
详细讲解与拓展
1. 没有为查询字段创建索引
如果查询字段没有索引,MongoDB 必然会执行全表扫描(COLLSCAN
)。
示例:
假设集合如下:
[
{ "name": "Alice", "age": 25 },
{ "name": "Bob", "age": 30 },
{ "name": "Charlie", "age": 35 }
]
查询:
db.users.find({ age: { $gt: 30 } }).explain("executionStats")
如果未为 age
创建索引:
{
"winningPlan": { "stage": "COLLSCAN" },
"executionStats": {
"totalDocsExamined": 3,
"totalKeysExamined": 0
}
}
解决方法:为 age
创建索引:
db.users.createIndex({ age: 1 })
2. 查询条件未命中索引
索引是有顺序的,查询条件必须满足索引的前缀规则,否则索引不会被使用。
示例:
创建复合索引:
db.users.createIndex({ name: 1, age: 1 })
以下查询会使用索引:
db.users.find({ name: "Alice" })
db.users.find({ name: "Alice", age: { $gt: 25 } })
以下查询不会使用索引(不满足前缀规则):
db.users.find({ age: { $gt: 25 } })
解决方法:调整查询条件,确保前缀字段 name
被使用。
3. 返回数据占比过高
如果查询返回的文档占集合总文档的很大比例(例如超过 30%-50%),MongoDB 会认为使用索引的成本大于全表扫描,改为执行全表扫描(COLLSCAN
)。
示例:
假设集合有 100,000 条文档,其中 90,000 条符合查询条件:
db.users.find({ status: "active" }).explain("executionStats")
即使为 status
字段创建了索引,MongoDB 可能仍会选择全表扫描,因为大部分文档都满足条件。
优化方法:
– 避免返回过多文档,尽量细化查询条件。
– 如果必须查询大量文档,可配合分页(limit
)优化查询性能。
4. 索引被禁用
使用 hint
方法,可以手动指定 MongoDB 使用或不使用特定索引。如果使用 { "$natural": 1 }
强制查询按照自然顺序扫描,则不会使用索引。
示例:
db.users.find({ age: { gt: 25 } }).hint({ "natural": 1 })
此查询将强制执行全表扫描。
5. 排序未优化
如果查询中的排序字段没有索引支持,MongoDB 会进行内存排序,这会消耗大量内存和 CPU 资源。
示例:
查询:
db.users.find({}).sort({ age: 1 }).explain("executionStats")
如果 age
字段没有索引,MongoDB 会执行内存排序。
解决方法:
为排序字段创建索引:
db.users.createIndex({ age: 1 })
6. 查询字段类型不匹配
如果查询条件的字段类型与索引字段的类型不一致,索引可能不会被使用。
示例:
假设索引字段 age
是数字类型:
db.users.createIndex({ age: 1 })
查询时使用字符串:
db.users.find({ age: "30" }).explain("executionStats")
此查询不会使用索引。
解决方法:
确保查询条件的字段类型与索引字段一致。
示例:如何分析索引未使用
通过 explain
方法可以分析查询是否使用了索引。
示例集合:
[
{ "name": "Alice", "age": 25 },
{ "name": "Bob", "age": 30 },
{ "name": "Charlie", "age": 35 }
]
- 查询时未使用索引:
db.users.find({ age: { $gt: 30 } }).explain("executionStats")
返回:
{ "winningPlan": { "stage": "COLLSCAN" }, "executionStats": { "totalDocsExamined": 3, "totalKeysExamined": 0 } }
- 创建索引并重新查询:
db.users.createIndex({ age: 1 }) db.users.find({ age: { $gt: 30 } }).explain("executionStats")
返回:
{ "winningPlan": { "stage": "IXSCAN" }, "executionStats": { "totalDocsExamined": 1, "totalKeysExamined": 1 } }
总结
MongoDB 不使用索引的常见原因包括未创建索引、查询条件不匹配、返回数据占比过高、排序未优化等。通过 explain
方法可以分析查询执行计划,定位问题并优化索引设计和查询条件,从而提高查询性能。合理设计索引结构是解决索引未使用问题的关键。