MongoDB 的 cursor 到底是怎么工作的?

2021-01-21 22:28:10 +08:00
 JasonLaw

首先使用以下代码初始化 collection

db.collection.drop()
for (i = 0; i < 100; i++) {
	db.collection.insertOne({x: Math.floor(i/5)});
}

然后执行var cursor = db.collection.find().batchSize(5).sort({x: 1})获取到 cursor,执行cursor.next()5 次,在执行第六次之前,执行db.collection.insertOne({x: 0}),这时执行cursor.next()的结果为{x: 1}

我的疑问

为什么cursor.next()的结果不是{x: 0}呢? cursor 到底是怎么工作的?它是怎么决定下次 batch 应该取哪些数据的呢?

相关资料

2284 次点击
所在节点    MongoDB
6 条回复
yagamil
2021-01-21 23:45:57 +08:00
sort 出来的是一个索引数据 temp_index,指向的你排序好的的数据,你后面再插入,不会改变之前 sort 的 temp_index
或者用一个更加简单的例子, 先 find(), 然后插入,再用 cursor next 到最后,看会不会找到你新插入的数据
JasonLaw
2021-01-22 11:21:36 +08:00
@yagamil #1

1. 我怎么查看 temp_index 呢?这个 temp_index 是特定于这个 cursor 的吗?会有一个类似 offset 的东西记录下次应该读取的位置吗?有没有相关的资料讲这个的?我找了好久都没有找到。
2. 我不是很理解“先 find(), 然后插入,再用 cursor next 到最后,看会不会找到你新插入的数据”。能够具体解释一下吗?
JasonLaw
2021-01-22 11:44:35 +08:00
@yagamil #1

1. 我在附言 1 描述了“使用 sort 和没有使用 sort 的区别”。使用了 sort 相当于需要在内存临时存储整个结果集吗?那么不是完全违背了使用 cursor 的初衷?
2. 在没有使用 sort 时,它是怎么决定下次 batch 应该获取到哪些数据的呢?在 primary index 上记录 cursor 的 offset 吗?
leopod1995
2021-01-22 13:14:17 +08:00
> 1. 我在附言 1 描述了“使用 sort 和没有使用 sort 的区别”。使用了 sort 相当于需要在内存临时存储整个结果集吗?那么不是完全违背了使用 cursor 的初衷?

因为你 sort 的 key 是 x 是没有建立索引的,所以才会需要在内存进行排序。
如何复现-> 给 x 加上索引,sort 的行为会保持一致。
> 2. 在没有使用 sort 时,它是怎么决定下次 batch 应该获取到哪些数据的呢?在 primary index 上记录 cursor 的 offset 吗?
没有使用 sort,取决于你的 query 。如果是例子中的 sql,会默认全表扫,也就是根据_id 。


### 总结
你的问题主要是不知道 Mongodb 的查询原理,建议多学习查询计划,或者多看源码。
mongodb 有个很重要的概念叫做 stage,`query`,'sort','fetch'都只是查询里面的一个 stage
JasonLaw
2021-01-22 14:09:35 +08:00
@leopod1995 #4 谢谢你的回复。猜到应该是这样的,不过还没有看到查询计划那块,接下来看看。
muzuiget
2021-01-22 14:29:41 +08:00
你的代码有两个问题。前提是你要知道 MongoDB 的数据是在另外一个进程里的,对你的程序来说,它就是一个服务器。

回到 cursor 问题,cursor 可以是一“会状态为了有个现实例子,快递站跟你说你有 10 个包要收,但是你家里只能存放 1 个包了,不能一次收下,等你先处理一个包,然后再收下一个,cursor.next() 就是服务器向你发下一条记录的意思。再假设你处理了两个包后,余下的 8 个包不想要了,直接退货,那么快递站不会再给你派送了,类似有 cursor.close() 余下的条目不要了。如果你的内存足够大,能有一次过收发所有记录,那 cursor 也有 toArray() 这种方法。

第二个问题,你的测试代码,你用 await 了吗,据我所知,MongoDB 的 NodeJS 接口都是异步了,调用函数后,未必马上执行对应的操作。

再说,枚举一个数组类型的容器过程时,就不应该同时修改该对象的元素长度,这样太容易出现不可预测的状态错误,你要仔细看相关文档看看这样做是否安全。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/747209

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX