如何使用索引进行排序在MongoDB中有效?

时间:2016-03-21 21:50:59

标签: mongodb sorting indexing time-complexity

我想知道如何使用索引进行排序实际上在MongoDB中有效。 MongoDB文档中有couple articles,但它们实际上并未描述排序的进展情况或时间复杂性。到目前为止,搜索SO和一般的互联网并没有发现任何相关的东西。

假设集合中有 a 文档,find()子句匹配 b 文档,返回 c 文档的限制, a >> b >> c c 是一些适当大的数字,因此返回的集合不能适合内存 - 例如,假设1M文档。

在操作开始时,存在需要进行排序的 b 文档以及文档将按以下顺序排序的特征 a 的排序树索引。

我可以想象:

A)按顺序遍历索引,并为每个ObjectID遍历 b 文档列表。返回匹配项,直到达到 c 。这将是O( ab )。

B)作为A),但首先在 b 文档中构建ObjectID的哈希集。这是O( a ),但需要O( b )内存。

我试过考虑基于遍历 b 文档集的排序,但似乎没有提出任何比O更快的事情( b log < em> b ),这并不比没有索引的排序好。

我假设(但也许我错了)每种类型都不需要索引扫描,那么排序实际上如何工作?

更新

凯文的回答和提供的链接缩小了这个问题的范围,但我想确认/澄清几点:

  1. 据我了解,如果要避免内存排序,则不能对查询和排序使用不同的索引。当我阅读this page时,它看起来好像你可以(或者至少,它没有指明一种方式或另一种方式),但这似乎是不正确的。本质上,文档是排序的,因为它们在查询期间按索引的顺序查找,因此按索引的顺序返回。正确?
  2. 查询复合索引时,排序索引必须是复合索引中的第一个索引,但查询相等的索引除外。如果不是,则在内存中执行排序。正确?
  3. 排序如何与$in$or查询一起使用?例如,假设查询是

    {a: {$in: [4, 6, 2, 1, 3, 10]}, b: {$gt: 1, $lt: 6}}

  4. ...并且按此顺序在ab上有一个复合索引。排序在ab的情况下,排序如何工作? $or更加复杂,因为据我所知,$or查询基本上分为多个单独的查询。 $or查询始终是内存中的排序,至少是为了合并单独查询的结果吗?

1 个答案:

答案 0 :(得分:18)

MongoDB中的索引存储在B树结构中,其中每个索引条目指向磁盘上的特定位置。使用B树结构也意味着MongoDB索引以排序顺序存储,总是按顺序遍历,并且MongoDB通过索引以排序顺序获取一系列文档很便宜。

查询中的SORT阶段(即内存中排序)限制为32MB内存使用。如果SORT阶段超出此限制,查询将失败。可以通过利用索引的排序特性来回避此限制,以便MongoDB可以返回带有sort()参数的查询,而无需执行内存中的排序。

我们假设查询的形状为:

    db.a.find({b:{$gt:100}, c:{$gt:200}}).sort(...)

集合a的索引为:

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

在查询中指定sort()阶段时,有两种可能的情况:

<强> 1。 MongoDB不能使用索引的排序特性,必须执行内存中SORT阶段。

如果查询不能使用&#34;索引前缀&#34;,则结果如此。例如:

    db.a.find({b:{$gt:100}, c:{$gt:200}}).sort({c:1})

在上面的查询中,索引{b:1,c:1}可用于:

  • 匹配查询b部分{b:{$gt:100}}大于100的文档。
  • 但是,无法保证返回的文档按c 进行排序。

因此,MongoDB别无选择,只能执行内存中排序。此查询的explain()输出将具有SORT阶段。此SORT阶段将限制为32MB内存使用。

<强> 2。 MongoDB可以使用索引的排序特性。

如果查询使用了以下结果:

  • 对与索引顺序匹配的键和
  • 进行排序
  • 指定与索引相同的顺序(即索引{b:1,c:1}可用于sort({b:1,c:1})sort({b:-1,c:-1}),但不能用于sort({b:1,c:-1})

例如:

    db.a.find({b:{$gt:100}, c:{$gt:200}}).sort({b:1})

在上面的查询中,索引{b:1,c:1}可用于:

  • 匹配查询b部分{b:{$gt:100}}大于100的文档。
  • 在这种情况下, MongoDB可以保证返回的文档按b 排序。

上述查询的explain()输出具有SORT阶段。此外,包含和不包含explain()的查询的sort()输出是相同的。实质上,我们免费获得sort()

理解这个主题的一个有价值的资源是Optimizing MongoDB Compound Indexes。请注意,这篇博客文章是在2012年写的。尽管某些术语可能已经过时,但该帖子的技术性仍然相关。

后续问题更新

  1. MongoDB使用only one index for most queries。例如,要避免查询中的内存SORT阶段

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

    索引必须同时涵盖ab字段;例如需要复合索引,例如{a:1,b:1}。您不能有两个单独的索引{a:1}{b:1},并且期望{a:1}索引用于相等部分,{b:1}索引用于排序部分。在这种情况下,MongoDB将选择两个索引中的一个。

    因此,对结果进行排序是正确的,因为它们是按照索引的顺序查找并返回的。

  2. 为避免使用复合索引进行内存中排序,索引的第一部分必须满足查询的相等部分第二部分必须迎合查询的排序部分(如上面(1)的说明所示)。

    如果您有这样的查询:

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

    索引{a:1,b:1}可用于排序部分(因为您基本上返回整个集合)。如果您的查询如下所示:

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

    同一索引{a:1,b:1}也可用于查询的两个部分。也:

    db.a.find({a:1,b:1})
    

    也可以使用相同的索引{a:1,b:1}

    请注意此处的模式:find()后跟sort()参数遵循索引顺序{a:1,b:1}。因此,复合索引必须通过相等来排序 - >排序