索引的覆盖查询
一、什么是覆盖查询
查询所需的所有字段都包含在索引中,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:不是。需要返回很多字段时,把所有字段都加入索引反而会导致索引体积过大,得不偿失。覆盖查询适合查询字段少、查询频繁的场景。