使用 MongoDB 索引

MongoDBMongoDBBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

在本实验中,你将学习使用 MongoDB 索引来优化查询性能和排序操作的基础知识。实验涵盖了几个关键步骤,包括使用索引进行查询、使用索引进行排序、检查索引覆盖、处理缺失索引以及移除未使用的索引。通过本实验,你将深入了解如何利用 MongoDB 的索引功能来提高数据库应用程序的效率。

实验首先创建一个示例集合,并演示如何在特定字段上创建索引以加速查询。然后,实验探讨了使用索引进行排序操作以及检查给定查询的索引覆盖情况。实验还涵盖了索引缺失的情况以及如何处理这些情况,以及移除未使用索引以维护数据库健康的过程。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL mongodb(("MongoDB")) -.-> mongodb/BasicOperationsGroup(["Basic Operations"]) mongodb(("MongoDB")) -.-> mongodb/QueryOperationsGroup(["Query Operations"]) mongodb(("MongoDB")) -.-> mongodb/IndexingGroup(["Indexing"]) mongodb(("MongoDB")) -.-> mongodb/AggregationOperationsGroup(["Aggregation Operations"]) mongodb/BasicOperationsGroup -.-> mongodb/update_document("Update Document") mongodb/BasicOperationsGroup -.-> mongodb/delete_document("Delete Document") mongodb/QueryOperationsGroup -.-> mongodb/find_documents("Find Documents") mongodb/QueryOperationsGroup -.-> mongodb/query_with_conditions("Query with Conditions") mongodb/QueryOperationsGroup -.-> mongodb/sort_documents("Sort Documents") mongodb/IndexingGroup -.-> mongodb/create_index("Create Index") mongodb/IndexingGroup -.-> mongodb/build_compound_index("Build Compound Index") mongodb/AggregationOperationsGroup -.-> mongodb/aggregate_group_totals("Aggregate Group Totals") subgraph Lab Skills mongodb/update_document -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/delete_document -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/find_documents -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/query_with_conditions -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/sort_documents -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/create_index -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/build_compound_index -.-> lab-422098{{"使用 MongoDB 索引"}} mongodb/aggregate_group_totals -.-> lab-422098{{"使用 MongoDB 索引"}} end

使用索引查询

在这一步骤中,你将学习如何使用索引来优化 MongoDB 中的查询性能。索引是一种特殊的数据结构,它以易于遍历的形式存储集合数据的一小部分,从而使数据库查询速度更快。

首先,让我们打开 MongoDB shell:

mongosh

接下来,我们创建一个包含一些数据的示例集合来演示索引:

use indexlab

db.users.insertMany([
    { name: "Alice", age: 28, city: "New York" },
    { name: "Bob", age: 35, city: "San Francisco" },
    { name: "Charlie", age: 42, city: "Chicago" },
    { name: "David", age: 25, city: "New York" },
    { name: "Eve", age: 31, city: "San Francisco" }
])

让我们在 age 字段上创建一个索引以加速查询:

db.users.createIndex({ age: 1 });

1 表示升序索引。现在,让我们运行一个查询并检查其性能:

db.users.find({ age: { $gt: 30 } }).explain("executionStats");

此命令将显示查询执行的详细信息,包括是否使用了索引。

示例输出:

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "indexName": "age_1"
      }
    }
  },
  "executionStats": {
    "executionTimeMillis": 0,
    "totalDocsExamined": 2,
    "totalKeysExamined": 2
  }
}

需要注意的关键点包括:

  • IXSCAN 表示使用了索引
  • totalDocsExamined 较低,表明索引帮助优化了查询
  • executionTimeMillis 非常短

使用索引排序

在这一步骤中,你将学习如何使用索引来优化 MongoDB 中的排序操作。对大型数据集进行排序可能会消耗大量计算资源,但通过正确的索引,我们可以显著提高性能。

让我们继续使用上一步中已有的数据库:

use indexlab

我们将创建一个复合索引,以实现高效的排序:

db.users.createIndex({ city: 1, age: -1 });

这个复合索引将帮助我们按城市升序和年龄降序进行排序。让我们运行一个查询来演示这一点:

db.users.find().sort({ city: 1, age: -1 }).explain("executionStats");

示例输出:

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "SORT",
      "sortPattern": { "city": 1, "age": -1 },
      "inputStage": {
        "stage": "IXSCAN",
        "indexName": "city_1_age_-1"
      }
    }
  },
  "executionStats": {
    "executionTimeMillis": 0,
    "totalDocsExamined": 5,
    "totalKeysExamined": 5
  }
}

现在,让我们查看实际的排序结果:

db.users.find().sort({ city: 1, age: -1 });

关键观察点:

  • 索引支持按多个字段排序
  • 1 表示升序
  • -1 表示降序
  • 查询使用索引以实现高效排序

检查索引覆盖

在这一步骤中,你将学习如何检查索引覆盖,并了解 MongoDB 如何使用索引来优化查询性能。索引覆盖可以帮助你确定查询是否充分利用了可用的索引。

让我们继续使用现有的数据库:

use indexlab

我们将创建一个更全面的索引来演示覆盖:

db.users.createIndex({ name: 1, city: 1, age: 1 });

现在,让我们运行一个可以完全使用索引的查询:

db.users.find({ name: "Alice", city: "New York" }).explain("executionStats");

示例输出:

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "indexName": "name_1_city_1_age_1"
      }
    }
  },
  "executionStats": {
    "totalDocsExamined": 1,
    "totalKeysExamined": 1,
    "indexesUsed": ["name_1_city_1_age_1"]
  }
}

让我们检查一个部分索引覆盖的查询:

db.users.find({ city: "New York" }).explain("executionStats");

关键观察点:

  • totalDocsExamined 显示检查了多少文档
  • totalKeysExamined 表示索引的效率
  • indexesUsed 显示使用了哪些索引

要列出集合中的所有索引:

db.users.getIndexes();

处理缺失索引

在这一步骤中,你将学习如何处理没有索引的查询,并了解 MongoDB 中缺失索引对性能的影响。

让我们继续使用现有的数据库:

use indexlab

首先,让我们运行一个没有索引的查询,看看它的性能:

db.users.find({ email: "alice@example.com" }).explain("executionStats");

示例输出:

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "COLLSCAN",
      "direction": "forward"
    }
  },
  "executionStats": {
    "totalDocsExamined": 5,
    "executionTimeMillis": 0
  }
}

关键观察点:

  • COLLSCAN 表示集合扫描(检查每个文档)
  • totalDocsExamined 显示检查了所有文档

让我们创建一个索引来提升此查询的性能:

db.users.createIndex({ email: 1 });

现在,再次运行相同的查询:

db.users.find({ email: "alice@example.com" }).explain("executionStats");

示例输出:

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "indexName": "email_1"
      }
    }
  },
  "executionStats": {
    "totalDocsExamined": 0,
    "totalKeysExamined": 0,
    "executionTimeMillis": 0
  }
}

关键改进点:

  • IXSCAN 替换了 COLLSCAN
  • totalDocsExamined 现在为 0
  • 查询性能显著提升

为了向现有用户添加 email 字段:

db.users.updateMany({}, [
  {
    $set: {
      email: {
        $concat: [{ $toLower: "$name" }, "@example.com"]
      }
    }
  }
]);

删除未使用的索引

在这一步骤中,你将学习如何识别并删除 MongoDB 中未使用的索引,以优化数据库性能和存储。

首先,查看集合中所有现有的索引:

use indexlab
db.users.getIndexes()

示例输出:

[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { age: 1 }, name: 'age_1' },
  { v: 2, key: { city: 1, age: -1 }, name: 'city_1_age_-1' },
  { v: 2, key: { name: 1, city: 1, age: 1 }, name: 'name_1_city_1_age_1' },
  { v: 2, key: { email: 1 }, name: 'email_1' }
]

要检查索引使用情况,我们可以使用 $indexStats 聚合操作:

db.users.aggregate([{ $indexStats: {} }]);

示例输出:

[
  {
    name: 'age_1',
    key: { age: 1 },
    host: '...',
    accesses: {
      ops: 0,
      since: ISODate("...")
    }
  },
  // ... 其他索引统计信息
]

让我们删除一个未使用的索引。我们将删除 city_1_age_-1 索引:

db.users.dropIndex("city_1_age_-1");

验证索引是否已被删除:

db.users.getIndexes();

索引管理的最佳实践:

  • 定期检查索引使用情况
  • 删除不常使用的索引
  • 在查询性能和存储开销之间找到平衡

总结

在本实验中,你学习了如何使用 MongoDB 索引来优化查询性能和排序操作。首先,你在 age 字段上创建了一个索引,并观察了索引如何加速基于 age 字段的查询。接着,你创建了一个复合索引,以实现对多个字段的高效排序。你还学习了如何检查索引覆盖、处理缺失索引,以及如何删除未使用的索引以维护健康的索引结构。