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(#) 背后的真相的主要内容,如果未能解决你的问题,请参考以下文章