查询优化
索引通过减少查询操作需要处理的数据量来提高读取操作的效率。这简化了在 MongoDB 中执行查询的相关工作。
创建索引以支持读取操作
如果您的应用程序查询特定字段或字段集合上的设立,则查询字段上的索引或字段设立上的复合索引可以阻止查询扫描整个集合以查找并返回查询结果。 有关索引的更多信息,请参阅MongoDB中索引的完整文档。
例子
某一应用程序将针对 type
字段来查询 inventory
集合。type
字段的值由用户决定。
var typeValue = <someUserInput>; db.inventory.find( { type: typeValue } );
要提高此查询的性能,请向type
字段上的inventory
集合添加升序或降序索引。 [ 1 ]在 mongosh
中,您可以使用db.collection.createIndex()
方法创建索引:
db.inventory.createIndex( { type: 1 } )
该索引可防止上述对 type
的查询扫描整个集合以返回结果。
如要分析使用索引进行查询的性能,请参阅解释“解释计划结果”。
除了优化读取操作之外,索引还支持排序操作并能够实现更高效的存储利用率。有关创建索引的更多信息,请参阅 db.collection.createIndex()
和 索引。
[1] | 对于单字段索引,升序和降序之间的选择并不重要。对于复合索引,选择很重要。详情请参见索引顺序。 |
查询选择性
查询选择性是指查询谓词排除或过滤出集合中文档的程度。查询选择性可以确定查询能否有效地使用索引,抑或根本不使用索引。
选择性更强的查询匹配的文档比例更小。例如,唯一的 _id
字段上的相等匹配具有高度选择性,因为它最多可以匹配一个文档。
选择性较低的查询会匹配更大比例的文档。选择性较低的查询无法有效使用索引,甚至根本不使用索引。
例如,不等于操作符 $nin
和 $ne
的选择性不高,因为它们通常匹配很大一部分索引。因此,在许多情况下,带有索引的 $nin
或 $ne
查询的性能可能不会比必须扫描集合中所有文档的 $nin
或 $ne
查询更好。
regular expressions
的选择性取决于表达式本身。有关详情,请参阅正则表达式和索引使用。
覆盖查询
覆盖查询是可以完全使用索引来满足并且不必检查任何文档的查询。满足以下所有条件时,索引将覆盖查询:
查询中的所有字段(包括应用程序指定的字段和内部需要的字段,例如出于分片目的)都是索引的一部分。
结果中返回的所有字段都位于同一索引中。
查询中没有字段等于
null
。例如,以下查询谓词无法生成覆盖查询:{ "field": null }
{ "field": { $eq: null } }
例子
inventory
集合在 type
和 item
字段上具有以下索引:
db.inventory.createIndex( { type: 1, item: 1 } )
该索引涵盖查询 type
和 item
字段并仅返回 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 } )
注意
要为嵌入式文档中的字段编制索引,请使用点表示法。请参阅在嵌入式字段上创建索引。
多键覆盖
多键索引如果要追踪哪个或哪些字段致使其成为多键,则其可涵盖对非数组字段的查询。
多键索引无法涵盖对数组字段的查询。
有关具有多键索引的覆盖查询的示例,请参阅多键索引页面上的覆盖查询。
性能
由于索引包含查询所需的所有字段,因此,MongoDB 可以只使用索引来匹配查询条件并返回结果。
仅查询索引比查询索引之外的文档要快得多。索引键通常小于它们编目的文档,并且索引通常在 RAM 中可用或按顺序存储在磁盘上。
限制
索引类型
并非所有索引类型都可以覆盖查询。有关覆盖索引支持的详细信息,请参阅相应索引类型的文档页面。
分片集合限制
explain
要确定查询是否属于覆盖查询,请使用 db.collection.explain()
或 explain()
方法。请参阅覆盖查询。