使用索引对查询结果进行排序

在本页面

在 MongoDB 中,排序操作可以通过基于索引中的 Sequences 检索文档来获得排序 Sequences。如果查询计划者无法从索引中获取排序 Sequences,它将对内存中的结果进行排序。与不使用索引的排序操作相比,使用索引的排序操作通常具有更好的性能。另外,不使用索引的排序操作在使用 32 MB 内存时将中止。

Note

由于 MongoDB 3.6 中对数组字段的排序行为发生了更改,因此在对以multikey index索引的数组上进行排序时,查询计划包括一个阻塞的 SORT 阶段。新的排序行为可能会对性能产生负面影响。

在阻塞式 SORT 中,必须在排序步骤中使用所有 Importing,然后才能产生输出。在非阻塞或“索引”排序中,排序步骤扫描索引以按请求的 Sequences 产生结果。

用单个字段索引排序

如果在单个字段上是升序或降序索引,则对该字段的排序操作可以是任意方向。

例如,在字段a上为集合records创建一个升序索引:

db.records.createIndex( { a: 1 } )

该索引可以支持对a的升序排序:

db.records.find().sort( { a: 1 } )

索引还可以通过以相反的 Sequences 遍历索引来支持a上的以下降序排序:

db.records.find().sort( { a: -1 } )

在多个字段上排序

创建一个compound index以支持对多个字段进行排序。

您可以在索引的所有键或子集上指定排序;但是,排序键必须以“在索引中出现的相同 Sequences”列出。例如,索引键样式{ a: 1, b: 1 }可以支持{ a: 1, b: 1 }上的排序,但不能支持{ b: 1, a: 1 }上的排序。

为了使查询使用复合索引进行排序,cursor.sort()文档中所有键的指定排序方向必须与索引键样式与索引键样式的倒数匹配。例如,索引键样式{ a: 1, b: -1 }可以支持{ a: 1, b: -1 }{ a: -1, b: 1 }上的排序,但不能支持{ a: -1, b: -1 }{a: 1, b: 1}上的排序。

排序和索引前缀

如果排序键对应于索引键或索引* prefix ,则 MongoDB 可以使用索引对查询结果进行排序。复合索引的前缀是在索引键模式的开头由一个或多个键组成的子集。

例如,在data集合上创建复合索引:

db.data.createIndex( { a:1, b: 1, c: 1, d: 1 } )

然后,以下是该索引的前缀:

{ a: 1 }
{ a: 1, b: 1 }
{ a: 1, b: 1, c: 1 }

以下查询和排序操作使用索引前缀对结果进行排序。这些操作不需要对内存中的结果集进行排序。

Example Index Prefix
db.data.find().sort( { a: 1 } ) { a: 1 }
db.data.find().sort( { a: -1 } ) { a: 1 }
db.data.find().sort( { a: 1, b: 1 } ) { a: 1, b: 1 }
db.data.find().sort( { a: -1, b: -1 } ) { a: 1, b: 1 }
db.data.find().sort( { a: 1, b: 1, c: 1 } ) { a: 1, b: 1, c: 1 }
db.data.find( { a: { $gt: 4 } } ).sort( { a: 1, b: 1 } ) { a: 1, b: 1 }

考虑以下示例,其中索引的前缀键同时出现在查询谓词和排序中:

db.data.find( { a: { $gt: 4 } } ).sort( { a: 1, b: 1 } )

在这种情况下,MongoDB 可以使用索引按排序指定的 Sequences 检索文档。如示例所示,查询谓词中的索引前缀可以与排序中的前缀不同。

索引的排序和非前缀子集

索引可以支持对索引键模式的非前缀子集进行排序操作。为此,查询必须在排序键之前的所有前缀键上包括“等于”条件。

例如,集合data具有以下索引:

{ a: 1, b: 1, c: 1, d: 1 }

以下操作可以使用索引来获取排序 Sequences:

Example Index Prefix
db.data.find( { a: 5 } ).sort( { b: 1, c: 1 } ) { a: 1 , b: 1, c: 1 }
db.data.find( { b: 3, a: 4 } ).sort( { c: 1 } ) { a: 1, b: 1, c: 1 }
db.data.find( { a: 5, b: { $lt: 3} } ).sort( { b: 1 } ) { a: 1, b: 1 }

如最后一个操作所示,只有索引字段(在排序子集之前)在查询文档中必须具有相等条件;其他索引字段可以指定其他条件。

如果查询未对排序规范之前或与之重叠的索引前缀指定“相等”条件,则该操作将“无效”使用索引。例如,以下操作将排序文档指定为{ c: 1 },但查询文档在前面的索引字段ab上不包含相等匹配项:

db.data.find( { a: { $gt: 2 } } ).sort( { c: 1 } )
db.data.find( { c: 5 } ).sort( { c: 1 } )

这些操作 将不会 有效地使用索引{ a: 1, b: 1, c: 1, d: 1 },甚至可能不会使用索引来检索文档。

索引使用和整理

要将索引用于字符串比较,操作还必须指定相同的排序规则。也就是说,如果排序规则的索引指定了不同的排序规则,则该索引不能支持对索引字段执行字符串比较的操作。

例如,集合myColl在具有排序规则语言环境"fr"的字符串字段category上具有索引。

db.myColl.createIndex( { category: 1 }, { collation: { locale: "fr" } } )

以下查询操作指定与索引相同的排序规则,可以使用索引:

db.myColl.find( { category: "cafe" } ).collation( { locale: "fr" } )

但是,默认情况下使用“简单”二进制整理程序的以下查询操作不能使用索引:

db.myColl.find( { category: "cafe" } )

对于索引前缀键不是字符串,数组和嵌入式文档的复合索引,指定其他排序规则的操作仍可以使用索引来支持索引前缀键的比较。

例如,集合myColl在数字字段scoreprice和字符串字段category上具有复合索引;使用排序规则语言环境"fr"创建索引以进行字符串比较:

db.myColl.createIndex(
   { score: 1, price: 1, category: 1 },
   { collation: { locale: "fr" } } )

以下使用"simple"二进制排序规则进行字符串比较的操作可以使用索引:

db.myColl.find( { score: 5 } ).sort( { price: 1 } )
db.myColl.find( { score: 5, price: { $gt: NumberDecimal( "10" ) } } ).sort( { price: 1 } )

以下操作将"simple"二进制排序规则用于在已索引的category字段上进行字符串比较,可以使用该索引来仅满足查询的score: 5部分:

db.myColl.find( { score: 5, category: "cafe" } )
首页