MongoDB:find(...).limit(#) 背后的真相

Posted

技术标签:

【中文标题】MongoDB:find(...).limit(#) 背后的真相【英文标题】:MongoDB: truth behind find(...).limit(#)MongoDB:find(...).limit(#) 背后的真相 【发布时间】:2014-09-16 22:30:08 【问题描述】:

在 MongoDB 中,.find(...).limit(#) 真的会限制查询的数量吗?

我的意思是,当你做db.collection.find(condition)时,查询所有符合给定条件的结果不是已经浪费了计算能力吗?如果是这样,那么在从查询结果中去除不需要的元素之后添加.limit() 吗?

非常感谢您澄清这一点!

【问题讨论】:

【参考方案1】:

db.collection.find 返回cursor,而不是结果数组或类似的数组。来自文档:

find() 方法“返回文档”时,该方法实际上是向文档返回cursor

当您迭代光标时,文档实际上是定位的。所以调用.limit 告诉光标什么时候说它已经完成了迭代。

更多关于游标的信息在这里:http://docs.mongodb.org/manual/core/cursors/#read-operations-cursors

【讨论】:

老问题,但仍需要一些澄清!在 NodeJS 中什么是“光标”?一个承诺 ?应该是因为可以等待: let cursor = User.find().limit(5); let array = await cursor;。但是cursor instanceof Promise 返回 false ! 好的,我明白了:"Thenable objects" can be awaited。因此,Mongo 游标不是一个承诺,而是一个提供 then() 方法的对象。晶莹剔透。 @JosephMerdrignac- 你可以await 任何值;如果该值不是 thenable,它什么也不做(除了等待一个异步“tick”)。我不使用 MongoDB,但在 documentation 中没有看到任何暗示游标是 thenables 的内容。他们有一些返回承诺的方法,但cursors themselves 似乎没有then 方法(无论如何,使用node-mongodb-native 驱动程序)。 你在 JS 中是对的,一切都可以等待,我正在使用 mongoose,NodeJS 驱动程序,其中查询是带有 then() 方法的对象。我什至找到了一些官方文档:mongoosejs.com/docs/promises.html#queries-are-not-promises 我忽略了“thenable”对象的概念(因此确信如果可以等待任何值,那么只有 Promise 实例的值会在整个过程中发生变化)。【参考方案2】:

limit() 不用于数据的后过滤。您可以使用 explain() 来解决这个问题。例如,我已经触发了 2 个查询 1)db.album.find().explain() 2) db.album.find().limit(5).explain();结果如下:

        > db.album.find().explain()
       
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 1000,
        "nscannedObjects" : 1000,
        "nscanned" : 1000,
        "nscannedObjectsAllPlans" : 1000,
        "nscannedAllPlans" : 1000,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 54,
        "nChunkSkips" : 0,
        "millis" : 12,
        "server" : "delbsinha25125:27017",
       "filterSet" : false
      

      > db.album.find().limit(5).explain()
        
         "cursor" : "BasicCursor",
         "isMultiKey" : false,
         "n" : 5,
         "nscannedObjects" : 5,
         "nscanned" : 5,
         "nscannedObjectsAllPlans" : 5,
         "nscannedAllPlans" : 5,
         "scanAndOrder" : false,
         "indexOnly" : false,
         "nYields" : 0,
         "nChunkSkips" : 0,
         "millis" : 0,
          "server" : "delbsinha25125:27017",
         "filterSet" : false
      

从上面的执行计划可以看出,有limit的只扫描了5个对象。

>

【讨论】:

当然这不是一个很好的例子,因为 nscanned 是 5 的原因是因为它实际上只需要读取 5,这是一种特殊情况,如果您有更复杂的查询,您会发现nscanned 可以更改超出限制【参考方案3】:

所以我在大约 5 天前试图回答这个问题,但后来发现了一些我必须实际调查的有趣的事情。

Limit 是在 sort 和 find 之后应用的,但不是在 mongod (MongoDB) 服务器本身内迭代这些结果之前,因此这意味着如果做得好,您可以使用 limit 节省大量计算能力。 @Bipul 的回答是一个完美的例子,但它并没有显示 MongoDB 在这里可以实现的真正限制。

应该注意的是,如果您使用的是最新版本的 MongoDB,实际上存在一个限制错误,它会导致扫描 etra 条目:https://jira.mongodb.org/browse/SERVER-14712 这是我在尝试回答这个问题时实际发现的事情之一.

需要注意的是,上面提到的bug只影响inde使用有限制。

现在考虑到@Bipuls 的回答,而没有独立使用,这个故事实际上有两个方面,一个是他所展示的,另一个也是如果你添加一个排序:

> db.rooms.find().sort(d:1).limit(2).explain()

        "clauses" : [
                
                        "cursor" : "BasicCursor",
                        "isMultiKey" : false,
                        "n" : 2,
                        "nscannedObjects" : 5,
                        "nscanned" : 5,
                        "scanAndOrder" : true,
                        "indexOnly" : false,
                        "nChunkSkips" : 0
                ,
                
                        "cursor" : "BasicCursor",
                        "isMultiKey" : false,
                        "n" : 0,
                        "nscannedObjects" : 0,
                        "nscanned" : 0,
                        "scanAndOrder" : true,
                        "indexOnly" : false,
                        "nChunkSkips" : 0
                
        ],
        "cursor" : "QueryOptimizerCursor",
        "n" : 2,
        "nscannedObjects" : 5,
        "nscanned" : 5,
        "nscannedObjectsAllPlans" : 5,
        "nscannedAllPlans" : 5,
        "scanAndOrder" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "server" : "ubuntu:27017",
        "filterSet" : false

集合的大小为 5,您可以看到在排序后应用了限制,因此它表明必须完全扫描集合,并且由于此查询没有 inde,因此它本来是完全 inde 扫描,而您的计算节省只不过是“看起来”。

现在,如果您添加一个 inde,这是不同的,它实际上可以使用 inde 的顺序来停止完整扫描并仅在您的限制范围内加载,但是,由于上述错误,它总是会再扫描一个比需要的多,但这是一个独立扫描,而不是正在加载的实际文档(取决于您的查询 find() 是否被覆盖)。

总而言之,如果使用得当,限制可以阻止 MongoDB 加载持续的文档,不仅可以节省您的工作集,还可以节省您的 IO 带宽。如果您可以正确使用限制与 inde 等,那么我肯定会推荐它。

【讨论】:

以上是关于MongoDB:find(...).limit(#) 背后的真相的主要内容,如果未能解决你的问题,请参考以下文章

mongodb----集合而定多种查询方式

MongoDB之Limit选取Skip跳过Sort排序

4.Mongodb数据查询2

MongoDB查询中的问题

MongoDB限制记录数

MongoDB Limit与Skip方法