我想知道如何使用索引进行排序实际上在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 ),这并不比没有索引的排序好。
我假设(但也许我错了)每种类型都不需要索引扫描,那么排序实际上如何工作?
更新
凯文的回答和提供的链接缩小了这个问题的范围,但我想确认/澄清几点:
排序如何与$in
或$or
查询一起使用?例如,假设查询是
{a: {$in: [4, 6, 2, 1, 3, 10]}, b: {$gt: 1, $lt: 6}}
...并且按此顺序在a
和b
上有一个复合索引。排序在a
或b
的情况下,排序如何工作? $or
更加复杂,因为据我所知,$or
查询基本上分为多个单独的查询。 $or
查询始终是内存中的排序,至少是为了合并单独查询的结果吗?
答案 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的文档。b
排序。上述查询的explain()
输出不具有SORT
阶段。此外,包含和不包含explain()
的查询的sort()
输出是相同的。实质上,我们免费获得sort()
。
理解这个主题的一个有价值的资源是Optimizing MongoDB Compound Indexes。请注意,这篇博客文章是在2012年写的。尽管某些术语可能已经过时,但该帖子的技术性仍然相关。
后续问题更新
MongoDB使用only one index for most queries。例如,要避免查询中的内存SORT
阶段
db.a.find({a:1}).sort({b:1})
索引必须同时涵盖a
和b
字段;例如需要复合索引,例如{a:1,b:1}
。您不能有两个单独的索引{a:1}
和{b:1}
,并且期望{a:1}
索引用于相等部分,{b:1}
索引用于排序部分。在这种情况下,MongoDB将选择两个索引中的一个。
因此,对结果进行排序是正确的,因为它们是按照索引的顺序查找并返回的。
为避免使用复合索引进行内存中排序,索引的第一部分必须满足查询的相等部分,第二部分必须迎合查询的排序部分(如上面(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}
。因此,复合索引必须通过相等来排序 - >排序强>