索引的覆盖查询


一、什么是覆盖查询

查询所需的所有字段都包含在索引中,MongoDB 直接从索引返回结果,不需要再去读取文档本身。

类比:在书的目录里就找到了所有需要的信息,不用再翻到正文。


二、实现覆盖查询的条件

  • 查询字段在索引中
  • 返回字段在索引中
  • 查询条件中不包含 _id(或明确排除 _id

三、示例

// 创建索引
db.users.createIndex({ age: 1, name: 1 })
 
// 覆盖查询:查询和返回的字段都在索引内
db.users.find(
  { age: { $gt: 18 } },
  { age: 1, name: 1, _id: 0 }  // 必须排除 _id,否则需要回文档取数据
)

四、如何验证是否命中覆盖查询

db.users.find(
  { age: { $gt: 18 } },
  { age: 1, name: 1, _id: 0 }
).explain("executionStats")

查看输出中的 totalDocsExamined

说明
totalDocsExamined: 0命中覆盖查询,没有读取任何文档
totalDocsExamined > 0未命中,仍然读取了文档

五、覆盖查询 vs 普通索引查询

对比普通索引查询覆盖查询
执行过程索引 → 找到文档位置 → 读取文档索引 → 直接返回结果
是否读取文档
性能较好最好

六、常见问题

Q:为什么排除 _id 才能命中覆盖查询? A:_id 默认会出现在返回结果中,而 _id 不在我们创建的索引里,MongoDB 就必须去文档中取 _id,导致覆盖查询失效。加上 _id: 0 明确排除后就不需要读取文档了。

Q:覆盖查询适合所有场景吗? A:不是。需要返回很多字段时,把所有字段都加入索引反而会导致索引体积过大,得不偿失。覆盖查询适合查询字段少、查询频繁的场景。