【问题标题】:How does sorting with an index work in MongoDB?在 MongoDB 中如何使用索引进行排序?
【发布时间】:2016-07-08 15:02:13
【问题描述】:

我想知道在 MongoDB 中使用索引进行排序实际上是如何工作的。 MongoDB 文档中有一个couplearticles,但它们实际上并没有描述排序如何进行或时间复杂度。到目前为止,对 SO 和整个互联网的搜索都没有发现任何相关内容。

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

在操作开始时,存在 b 个需要排序的文档,以及一个大小为 a 的排序树索引,用于对文档进行排序的特征.

我可以想象:

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

B) 与 A) 相同,但首先在 b 文档中构建 ObjectID 的哈希集。这是 O(a),但占用 O(b) 内存。

我尝试考虑基于遍历 b 文档集的排序,但似乎无法提出比 O(b log b),这并不比没有索引的排序好。

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

更新:

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

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

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

...在ab 上按顺序有一个复合索引。在ab 上进行排序的情况下,排序将如何工作? $or 更加复杂,因为据我了解,$or 查询基本上分为多个单独的查询。 $or 查询是否总是在内存中排序,至少用于合并单独查询的结果?

【问题讨论】:

    标签: mongodb sorting indexing time-complexity


    【解决方案1】:

    MongoDB 中的索引存储在 B 树结构中,其中每个索引条目都指向磁盘上的特定位置。使用 B 树结构还意味着 MongoDB 索引按排序顺序存储,始终按顺序遍历,并且 MongoDB 通过索引按排序顺序获取一系列文档的成本很低。

    更新:B-tree 结构适用于 MMAPv1 存储引擎,但 WiredTiger 存储引擎的实现略有不同(自 MongoDB 3.2 起默认)。基本思想保持不变,按排序顺序遍历索引很便宜。

    查询中的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 阶段

    这是查询不能使用“索引前缀”时的结果。例如:

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

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

    • 为查询的{b:{$gt:100}} 部分匹配b 大于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:{$gt:100}} 部分匹配b 大于100 的文档。
    • 在这种情况下,MongoDB 可以保证返回的文档按照b进行排序

    上述查询的explain() 输出将具有SORT 阶段。此外,带有和不带有 sort() 的查询的 explain() 输出是相同的。从本质上讲,我们免费获得了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}。因此,复合索引必须按 equality -> sort 排序。

    关于不同类型排序的更新

    如果一个字段在文档之间具有不同的类型(例如,如果 a 在一个文档中是字符串,在其他文档中是数字,在另一个文档中是布尔值),那么排序如何进行?

    答案是MongoDB BSON type comparison order。套用手册页,顺序是:

    1. MinKey(内部类型)
    2. 数字(整数、长整数、双精度、小数)
    3. 符号、字符串
    4. 对象
    5. 数组
    6. BinData
    7. 对象标识
    8. 布尔值
    9. 日期
    10. 时间戳
    11. 正则表达式
    12. MaxKey(内部类型)

    所以从上面使用升序的例子来看,包含数字的文档将首先出现,然后是字符串,然后是布尔值。

    【讨论】:

    • 这很奇怪,我们所有的 cmets 都消失了。无论如何,问题的 $in / $ 或部分是here
    • 知道了,我会尽快回复。
    • 我在试图对其执行排序的集合上有索引,但是当我编写查询并检查 explain() 的结果时,我仍然获得了作为 { "stage" : " SKIP”,“skipAmount”:82560,“inputStage”:{“stage”:“SORT”,“sortPattern”:{“start_time”:1},“limitAmount”:82570,“inputStage”:{“stage”:“ SORT_KEY_GENERATOR", "inputStage" : { "stage" : "COLLSCAN", "filter" : { "ID" : { "$eq" : "someID" } }, "direction" : "forward" } } },
    • @AnoopGoudar 请创建一个新问题,而不是在评论部分提问。
    • 我创建了一个新问题。找到下面的链接stackoverflow.com/questions/48616325/…
    猜你喜欢
    • 1970-01-01
    • 2011-12-04
    • 2018-09-01
    • 2020-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 2015-02-20
    相关资源
    最近更新 更多