创建 MongoDB 索引

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习在 MongoDB 中创建和管理索引的基础技术。索引对于优化数据库性能至关重要,因为它们能让数据库比扫描集合中的每个文档更快地查找和检索数据。你将练习创建单字段、复合和唯一索引,分析它们对查询性能的影响,并管理它们的生命周期。完成本实验后,你将对如何使用索引提高 MongoDB 查询效率有扎实的理解。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 100%。获得了学习者 93% 的好评率。

创建单字段索引

在这一步,你将连接到 MongoDB,创建一个包含示例集合的数据库,然后在一个字段上创建你的第一个索引。单字段索引可以提高对该特定字段进行过滤或排序的查询的性能。

首先,在终端输入 mongosh 来打开 MongoDB Shell。这将把你连接到你环境中运行的 MongoDB 服务器。

mongosh

进入 MongoDB Shell 后,你将看到一个 > 提示符。让我们创建一个名为 indexlab 的新数据库并切换到它。本实验中后续的所有命令都将在该 shell 中运行,除非另有说明。

use indexlab

现在,让我们向一个名为 users 的新集合中插入一些示例文档。这些数据将在整个实验中使用。

db.users.insertMany([
  { name: "Alice", age: 28, email: "alice@example.com" },
  { name: "Bob", age: 35, email: "bob@example.com" },
  { name: "Charlie", age: 42, email: "charlie@example.com" }
]);

数据准备就绪后,让我们在 name 字段上创建一个索引。单字段索引有助于加速通过该字段搜索文档的查询。

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

在上面的命令中,{ name: 1 } 指定了在 name 字段上按升序创建索引。使用 -1 将创建降序索引。

为了验证索引是否已创建,你可以列出 users 集合的所有索引。

db.users.getIndexes();

你应该在输出中看到两个索引。一个是 MongoDB 在每个集合上创建的默认 _id 索引,另一个是你刚刚创建的 name_1 索引。

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "name": 1 }, "name": "name_1" }
]

构建复合索引

虽然单字段索引很有用,但许多查询会过滤多个字段。对于这些情况,包含多个字段的复合索引可以提供显著的性能提升。在这一步,你将创建一个复合索引。

让我们继续在 mongosh shell 中操作。我们将创建一个在 agename 字段上的复合索引。复合索引中字段的顺序很重要。MongoDB 可以使用此索引来支持仅按 age 查询,或同时按 agename 查询。

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

此命令创建一个索引,该索引首先按降序 (-1) 对文档按 age 进行排序,然后对于具有相同年龄的文档,按升序 (1) 对它们按 name 进行排序。

让我们添加一些文档,使我们的集合在查询时更加多样化。

db.users.insertMany([
  { name: "David", age: 28, email: "david@example.com" },
  { name: "Eve", age: 35, email: "eve@example.com" }
]);

现在,再次查看索引列表,以查看你的新复合索引。

db.users.getIndexes();

输出现在将包含 age_-1_name_1 索引,以及之前的索引。

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "name": 1 }, "name": "name_1" },
  { "v": 2, "key": { "age": -1, "name": 1 }, "name": "age_-1_name_1" }
]

此复合索引将有效地服务于按 age 进行过滤或排序,或者先按 age 再按 name 进行过滤或排序的查询。

创建唯一索引

索引还可以用于强制执行数据完整性。唯一索引确保被索引的字段(或字段)不包含重复值。在这一步,你将创建一个唯一索引,以防止 users 集合中出现重复的电子邮件地址。

让我们在 email 字段上创建一个唯一索引。这可以通过在创建索引时添加 { unique: true } 选项来完成。

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

现在唯一索引已就位,如果插入或更新文档导致 email 字段的值重复,MongoDB 将拒绝该操作。

让我们来测试一下。首先,尝试插入一个具有新、唯一电子邮件的文档。这应该会成功。

db.users.insertOne({ name: "Frank", age: 31, email: "frank@example.com" });

接下来,尝试插入一个具有现有电子邮件的文档,例如 alice@example.com。此操作将失败。try...catch 块允许你查看错误,而不会断开与 mongosh shell 的连接。

try {
  db.users.insertOne({ name: "Fiona", age: 29, email: "alice@example.com" });
} catch (e) {
  print(e);
}

该命令将抛出一个错误,指示存在重复键冲突。输出将包含类似 E11000 duplicate key error collection 的消息。

你可以再次查看索引,以查看唯一约束属性。

db.users.getIndexes();

请注意输出中 email_1 索引上的 unique: true 属性。

[
  ...,
  {
    v: 2,
    key: { email: 1 },
    name: 'email_1',
    unique: true
  }
]

使用 explain() 分析索引使用情况

创建索引只是成功的一半;你还需要验证 MongoDB 是否确实在查询中使用它们。explain() 方法是一个强大的工具,可以提供查询执行方式的详细信息。本步骤将展示如何使用 explain() 来验证 MongoDB 是否有效地使用了你现有的索引。

让我们分析一个查找特定年龄用户的查询。由于我们已经有了上一步创建的复合索引 age_-1_name_1,MongoDB 可以使用此索引来优化 age 字段上的查询。

db.users.find({ age: 35 }).explain("executionStats");

在输出中,查找 winningPlan 内的 executionStats.stage 字段。你应该会看到一个值为 IXSCAN 的字段,它代表“索引扫描”(Index Scan)。这表明 MongoDB 正在使用现有的复合索引 age_-1_name_1 来快速定位相关文档。你也应该看到 totalDocsExamined 与返回的文档数量相匹配,这证明了使用复合索引的效率。

为了更好地理解 MongoDB 如何选择索引,让我们也测试一个可以受益于我们之前创建的单字段 name 索引的查询。

db.users.find({ name: "Alice" }).explain("executionStats");

此查询也应该显示 IXSCAN 作为获胜计划阶段,确认 MongoDB 正在使用我们在第一步中创建的 name_1 索引。

查看和删除索引

管理索引的最后一部分是了解如何列出它们以及在不再需要时删除它们。未使用的索引仍然会消耗存储空间并增加写操作的开销,因此清理它们是一个好习惯。

首先,让我们列出你在 users 集合中迄今为止创建的所有索引。

db.users.getIndexes();

此命令提供了当前索引设置的全面概述。假设你决定不再需要复合索引 age_-1_name_1。你可以使用 dropIndex() 方法并指定索引名称来删除它。

db.users.dropIndex("age_-1_name_1");

运行该命令后,你将收到一条确认消息。为了确保万无一失,你可以再次列出索引以验证它已被删除。

db.users.getIndexes();

age_-1_name_1 索引应该不再出现在列表中。

如果你需要从集合中删除所有自定义索引(除了默认的 _id 索引),你可以使用 dropIndexes() 方法。此命令功能强大,请谨慎使用。

// Example: db.users.dropIndexes()

至此,MongoDB 中的索引管理基本操作就完成了。你现在可以退出 mongosh shell。

exit;

总结

在本实验中,你学习了使用 MongoDB 索引的基本技能。你首先创建了一个基本的单字段索引来加速简单查询。然后,你进阶构建了一个复合索引来优化涉及多个字段的查询。你还学会了如何通过创建唯一索引来强制执行数据完整性。此外,你使用了 explain() 方法来分析查询计划,并确认你的索引正在被有效使用,观察了集合扫描和索引扫描之间的性能差异。最后,你通过列出和删除索引来练习管理你的索引。掌握这些索引技术是构建快速可扩展的 MongoDB 应用程序的关键一步。