Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs 菜单
Docs 主页
/ /

查询优化

查询优化减少了查询必须进程的数据量。使用索引、投影和查询限制来提高性能并减少资源消耗。随着集合的增长,定期查看查询性能,以确定何时进行扩展。

索引将字段值存储在单独的数据结构中。在读取操作中, MongoDB搜索索引而不是扫描整个集合。在写入操作中, MongoDB同时更新集合和索引。

为最常见的查询创建索引。如果查询搜索多个字段,请创建复合索引。

示例,考虑对 inventory集合中的 type字段执行以下查询:

let typeValue = <someUserInput>;
db.inventory.find( { type: typeValue } );

要提高此查询的性能,请在 type字段上添加索引。

db.inventory.createIndex( { type: 1 } )

[1]mongosh 中,使用 db.collection.createIndex()

要分析查询性能,请参阅解释解释计划结果。

[1] 对于单字段索引,索引的顺序并不重要。对于复合索引,字段顺序会影响索引支持的查询。有关详细信息,请参阅复合索引排序顺序。

查询选择性衡量查询谓词过滤文档的程度,并确定查询是否可以有效地使用索引。

  • 高选择性查询匹配更少的文档,并更有效地使用索引。实例,_id 上的等值匹配具有高度选择性,因为它最多可以匹配一个文档。

  • 选择性较低的查询会匹配更多文档,但会降低索引的使用效率。

例如,不等于操作符 $nin$ne 的选择性高,因为它们通常匹配很大一部分索引。因此,在许多情况下,带有索引的 $nin$ne 查询的性能可能不会比必须扫描集合中所有文档的 $nin$ne 查询更好。

regular expression 的选择性取决于表达式本身。有关详细信息,请参阅正则表达式和索引使用。

当您需要文档中的部分字段时,可以通过仅返回所需的字段来提高性能。投影可减少网络流量和处理时间。

示例,假设以下查询仅返回 timestamptitleauthorabstract 字段。

db.posts.find(
{},
{ timestamp : 1, title : 1, author : 1, abstract : 1}
).sort( { timestamp : -1 } )

当您使用$project聚合阶段时,它通常应该是管道中的最后一个阶段,用于指定要返回给客户端的字段。

在管道的开头或中间使用 $project 阶段来减少传递到后续管道阶段的字段数量不太可能提高性能,因为数据库会自动执行此优化。

有关详细信息,请参阅要从查询返回的项目字段。

要实现覆盖查询,请对投影字段索引。ESR(相等、排序、范围)规则适用于索引中字段的顺序。

示例,考虑 inventory集合上的以下索引:

db.inventory.createIndex( { type: 1, _id: 1, price: 1, item: 1, expiryDate: 1} )

上述查询虽然在技术上是正确的,但其结构并未优化查询性能。

以下查询应用 ESR 规则以实现更高效的复合索引:

db.inventory.aggregate([
{ $match: {type: "food", expiryDate: { $gt: ISODate("2025-07-10T00:00:00Z") }}},
{ $sort: { item: 1 }},
{ $project: { _id: 1, price: 1} }
])

索引和查询遵循 ESR 规则:

  • type 用于等值匹配(E),因此它是索引}中的第一个字段。

  • item 用于排序 (S),因此在索引中位于 type 之后。

  • expiryDate 用于范围查询(R ),因此它是索引中的最后一个字段。

MongoDB 游标 分批返回结果。如果知道需要多少结果,请将该值传递给 limit() 方法以减少网络资源使用量。

限制排序后的结果,以便您知道返回了哪些文档。示例,以下查询仅返回 posts集合中的 10 个最新结果:

db.posts.find().sort( { timestamp : -1 } ).limit(10)

有关更多信息,请参阅 limit()

查询优化器会为特定操作选择最佳索引。但是,您可以使用 hint() 方法强制执行特定索引。这对于性能测试或当一个字段出现在多个索引中并且您需要保证MongoDB使用哪个索引非常有用。

使用 $inc 操作符递增或递减文档中的值。该操作符在服务器端递增字段值,作为选择文档、在客户端进行简单修改,然后将文档写入服务器的替代方法。此外,当多个应用程序程序实例同时更新同一字段时,该操作符可以防止竞争条件。

覆盖查询是查询可以完全由索引满足而无需检查任何文档的查询。当满足以下所有条件时,索引将覆盖查询:

  • 查询中的所有字段(包括应用程序指定的字段以及内部所需的任何字段,例如用于分片)都是索引的一部分。

  • 结果中返回的所有字段都位于同一索引中。

  • 查询中没有字段等于 null。例如,以下查询谓词无法生成覆盖查询:

    • { "field": null }

    • { "field": { $eq: null } }

inventory集合在 typeitem 字段上具有以下索引:

db.inventory.createIndex( { type: 1, item: 1 } )

该索引涵盖以下查询,该查询根据 typeitem 进行筛选并仅返回 item

db.inventory.find(
{ type: "food", item:/^c/ },
{ item: 1, _id: 0 }
)

为了使指定索引覆盖查询,投影文档必须显式指定 _id: 0 以从结果中排除 _id 字段,因为索引不包含 _id 字段。

索引可以涵盖对嵌入式文档中字段的查询。

示例,考虑以下 userdata集合:

db.userdata.insertOne(
{ _id: 1, user: { login: "tester" } }
)

该集合包含以下索引:

db.userdata.createIndex(
{ "user.login": 1 }
)

{ "user.login": 1 } 索引涵盖以下查询:

db.userdata.find(
{ "user.login": "tester" },
{ "user.login": 1, _id: 0 }
)

注意

要为嵌入式文档中的字段编制索引,请使用点表示法。请参阅在嵌入式字段上创建索引

如果索引追踪哪个或哪些字段使其成为多键,则多键索引可以涵盖对非数组字段的查询。

多键索引无法涵盖对数组字段的查询。

有关示例,请参阅多键索引页面上的涵盖查询

涵盖的查询匹配查询条件并仅使用索引返回结果。这比获取文档更快,因为索引键通常小于文档,并且索引通常位于RAM中或按顺序存储在磁盘上。

并非所有索引类型都支持覆盖查询。请参阅特定索引类型的文档。

mongos 上运行时,如果索引包含分片键,则索引只能涵盖对分片集合的查询。

要检查查询是否被覆盖,请使用 db.collection.explain()explain()。请参阅涵盖的查询

后退

因果一致性以及读关注和写关注

获得技能徽章

免费掌握“查询优化”!

了解详情

在此页面上