MongoDB何时不使用索引 ?

参考回答

在以下几种情况下,MongoDB 查询可能不会使用索引:

  1. 没有为查询字段创建索引:如果查询字段上没有索引,MongoDB 会执行全表扫描(COLLSCAN)。
  2. 查询条件未命中索引:查询条件未与索引字段匹配或未按照索引的顺序使用。
  3. 返回数据占比过高:如果查询结果占文档总数的很大比例,MongoDB 可能会放弃使用索引,改为全表扫描。
  4. 索引被禁用:使用 hint 明确指定不使用索引。
  5. 排序未优化:如果排序字段没有索引支持,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 }
]
  1. 查询时未使用索引:
    db.users.find({ age: { $gt: 30 } }).explain("executionStats")
    

    返回:

    {
       "winningPlan": { "stage": "COLLSCAN" },
       "executionStats": {
           "totalDocsExamined": 3,
           "totalKeysExamined": 0
       }
    }
    
  2. 创建索引并重新查询:
    db.users.createIndex({ age: 1 })
    db.users.find({ age: { $gt: 30 } }).explain("executionStats")
    

    返回:

    {
       "winningPlan": { "stage": "IXSCAN" },
       "executionStats": {
           "totalDocsExamined": 1,
           "totalKeysExamined": 1
       }
    }
    

总结

MongoDB 不使用索引的常见原因包括未创建索引、查询条件不匹配、返回数据占比过高、排序未优化等。通过 explain 方法可以分析查询执行计划,定位问题并优化索引设计和查询条件,从而提高查询性能。合理设计索引结构是解决索引未使用问题的关键。

发表评论

后才能评论