首先使用以下代码初始化 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 应该取哪些数据的呢?
当我将var cursor = db.collection.find().batchSize(5).sort({x: 1})
修改为var cursor = db.collection.find().batchSize(5)
时,cursor能够获取到对collection的改变。以下步骤可以证明。
首先使用以下代码初始化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)
获取到cursor
,执行cursor.next()
5次,在执行第六次之前,执行db.collection.deleteMany({x: 1})
,这时执行cursor.next()
的结果为{x: 2}
。
1
yagamil 2021-01-21 23:45:57 +08:00
sort 出来的是一个索引数据 temp_index,指向的你排序好的的数据,你后面再插入,不会改变之前 sort 的 temp_index
或者用一个更加简单的例子, 先 find(), 然后插入,再用 cursor next 到最后,看会不会找到你新插入的数据 |
2
JasonLaw OP @yagamil #1
1. 我怎么查看 temp_index 呢?这个 temp_index 是特定于这个 cursor 的吗?会有一个类似 offset 的东西记录下次应该读取的位置吗?有没有相关的资料讲这个的?我找了好久都没有找到。 2. 我不是很理解“先 find(), 然后插入,再用 cursor next 到最后,看会不会找到你新插入的数据”。能够具体解释一下吗? |
3
JasonLaw OP @yagamil #1
1. 我在附言 1 描述了“使用 sort 和没有使用 sort 的区别”。使用了 sort 相当于需要在内存临时存储整个结果集吗?那么不是完全违背了使用 cursor 的初衷? 2. 在没有使用 sort 时,它是怎么决定下次 batch 应该获取到哪些数据的呢?在 primary index 上记录 cursor 的 offset 吗? |
4
leopod1995 2021-01-22 13:14:17 +08:00 1
> 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 |
5
JasonLaw OP @leopod1995 #4 谢谢你的回复。猜到应该是这样的,不过还没有看到查询计划那块,接下来看看。
|
6
muzuiget 2021-01-22 14:29:41 +08:00 1
你的代码有两个问题。前提是你要知道 MongoDB 的数据是在另外一个进程里的,对你的程序来说,它就是一个服务器。
回到 cursor 问题,cursor 可以是一“会状态为了有个现实例子,快递站跟你说你有 10 个包要收,但是你家里只能存放 1 个包了,不能一次收下,等你先处理一个包,然后再收下一个,cursor.next() 就是服务器向你发下一条记录的意思。再假设你处理了两个包后,余下的 8 个包不想要了,直接退货,那么快递站不会再给你派送了,类似有 cursor.close() 余下的条目不要了。如果你的内存足够大,能有一次过收发所有记录,那 cursor 也有 toArray() 这种方法。 第二个问题,你的测试代码,你用 await 了吗,据我所知,MongoDB 的 NodeJS 接口都是异步了,调用函数后,未必马上执行对应的操作。 再说,枚举一个数组类型的容器过程时,就不应该同时修改该对象的元素长度,这样太容易出现不可预测的状态错误,你要仔细看相关文档看看这样做是否安全。 |