V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐工具
RoboMongo
推荐书目
50 Tips and Tricks for MongoDB Developers
Related Blogs
Snail in a Turtleneck
JasonLaw
V2EX  ›  MongoDB

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

  •  
  •   JasonLaw · 2021-01-21 22:28:10 +08:00 · 2412 次点击
    这是一个创建于 1420 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先使用以下代码初始化 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 应该取哪些数据的呢?

    相关资料

    第 1 条附言  ·  2021-01-22 11:36:23 +08:00

    当我将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}

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

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

    1. 我在附言 1 描述了“使用 sort 和没有使用 sort 的区别”。使用了 sort 相当于需要在内存临时存储整个结果集吗?那么不是完全违背了使用 cursor 的初衷?
    2. 在没有使用 sort 时,它是怎么决定下次 batch 应该获取到哪些数据的呢?在 primary index 上记录 cursor 的 offset 吗?
    leopod1995
        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
    JasonLaw
        5
    JasonLaw  
    OP
       2021-01-22 14:09:35 +08:00 via iPhone
    @leopod1995 #4 谢谢你的回复。猜到应该是这样的,不过还没有看到查询计划那块,接下来看看。
    muzuiget
        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 接口都是异步了,调用函数后,未必马上执行对应的操作。

    再说,枚举一个数组类型的容器过程时,就不应该同时修改该对象的元素长度,这样太容易出现不可预测的状态错误,你要仔细看相关文档看看这样做是否安全。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   882 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 20:02 · PVG 04:02 · LAX 12:02 · JFK 15:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.