OPTIONS
翻译或纠错本页面

创建索引以支持查询

当索引包含了查询的所有键时,索引可以支持该查询。查询会扫描索引而不是集合。创建可以支持查询的索引会带来极大的查询性能提升。

本文描述了创建支持查询的索引的策略。

如果所有的查询都使用同样的单个键时,创建一个单键索引

如果您在某个集合上只查询某个单键,那么您可以在这个集合上创建一个单键索引。例如,您可能在 product 集合的 category 键上创建索引:

db.products.ensureIndex( { "category": 1 } )

创建复合索引以支持几个不同的查询

如果您有时需要查询某个键,而有时又需要查询同样的键和额外的键,那么和创建单键索引相比,创建复合索引会是更高效的选择。MongoDB都会使用复合索引来处理这两种请求。例如,您可能会在 categoryitem 键上创建索引:

db.products.ensureIndex( { "category": 1, "item": 1 } )

这允许您有两种选择。您可以只查询 category 或者同时查询 categoryitem 。一个在多个键上创建的 复合索引 可以支持那些搜索被索引键的 “前缀” 子集的查询。

例子

在某集合上有如下索引:

{ x: 1, y: 1, z: 1 }

它可以支持如下索引支持的查询:

{ x: 1 }
{ x: 1, y: 1 }

在某些情况下,前缀键的索引可能会有更好的查询性能:例如,如果 z 是一个很大的数组。

索引 { x: 1, y: 1, z: 1 } 也支持许多如下索引支持的查询:

{ x: 1, z: 1 }

此外, { x: 1, z: 1 } 索引还有其它用途。给定如下查询:

db.collection.find( { x: 5 } ).sort( { z: 1} )

索引 { x: 1, z: 1 } 同时支持查询和排序操作,但是索引 { x: 1, y: 1, z: 1 } 只支持查询。参见 使用索引来排序查询结果 了解更多关于排序的详细信息。

从版本2.6开始,MongoDB可以使用 索引交集 来匹配查询。创建支持您的查询的复合索引,还是依靠索引交集来匹配查询,这取决于您的系统的具体情况。参见 索引交集和复合索引 了解更多细节。

创建索引以支持被覆盖的查询(Covered Queries)

被覆盖的查询是指:

  • 查询 中的所有键都是索引的一部分, 并且

  • 所有结果集中返回的键也都在同一个索引中。

由于索引 “覆盖” 了查询,MongoDB只需要使用就可以匹配 查询条件 并且 返回结果集。MongoDB只需要在索引中,而不需要再查看文档,就可以满足查询。

和查询索引之外的文档相比, 在索引中查询会更快。索引键一般都小于被索引的文档,而且索引一般都在内存中直接可用或者在磁盘上顺序存储。

如果可能的话,MongoDB会自动使用能覆盖查询的索引。为了确保索引能 覆盖 查询,可以创建一个包含了 查询文档 和结果中所有键的索引。您可以在查询中通过 映射 选项来指定返回的键。默认情况下,MongoDB会在查询结果中包含 _id 键。索引,如果索引 不包含 _id 键,您可以从结果中排除 _id 键。

例子

假设集合 usersuserstatus 键上有一个索引,创建的选项如下:

db.users.ensureIndex( { status: 1, user: 1 } )

那么,索引会覆盖这样一个搜索 status 键只返回 user 的查询:

db.users.find( { status: "A" }, { user: 1, _id: 0 } )

在这个操作中,映射选项显式地指定 _id: 0 来将 _id 从结果中排除,因为索引只包含 statususer

如果映射选项没有指定要排除 _id ,查询会返回 _id 键。如下查询将 不会 被基于 statususer 的索引所覆盖,因为其映射 { user: 1 } ,查询会同时返回 user_id :

db.users.find( { status: "A" }, { user: 1 } )

如果遇到如下情况,索引 无法 覆盖查询:

  • 在集合的任一文档中任一被索引键包含了数组。如果被索引键是数组,那么索引就会变成 多键索引 且不支持覆盖查询。

  • 任一被索引键是子文档中的键。如果需要索引子文档中的键,可以使用 dot notation 。例如,假设集合 users 中的文档格式如下:

    { _id: 1, user: { login: "tester" } }
    

    集合中有如下索引:

    { user: 1 }
    
    { "user.login": 1 }
    

    索引 { user: 1 } 可以覆盖如下查询:

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

    但是 { "user.login": 1 } 无法 覆盖如下查询:

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

    但是,查询会使用 { "user.login": 1 } 索引来寻找匹配的文档。

如果想确定查询是否被覆盖了,可以使用 explain() 方法。如果 explain() 的输出中 indexOnly 显示 true ,那么查询被一条索引覆盖了,此时MongoDB只会查询该索引 返回返回结果。

参见 衡量索引的使用情况 了解更多信息。