【问题标题】:pymongo.errors.CursorNotFound: cursor id '...' not valid at serverpymongo.errors.CursorNotFound: cursor id '...' 在服务器上无效
【发布时间】:2014-08-03 16:23:46
【问题描述】:

我正在尝试使用以下代码获取 mongo 数据库中存在的一些 id:

client = MongoClient('xx.xx.xx.xx', xxx)
db = client.test_database
db = client['...']
collection = db.test_collection
collection = db["..."]


for cursor in collection.find({ "$and" : [{ "followers" : { "$gt" : 2000 } }, { "followers" : { "$lt" : 3000 } }, { "list_followers" : { "$exists" : False } }] }): 
    print cursor['screenname']
    print cursor['_id']['uid']
    id = cursor['_id']['uid']

但是,过了一会儿,我收到了这个错误:

pymongo.errors.CursorNotFound: cursor id '...' 在服务器上无效。

我发现这个article 指的是那个问题。然而,我不清楚采取哪种解决方案。是否可以使用find().batch_size(30)?上面的命令具体是做什么的?我可以使用 batch_size 获取所有数据库 ID 吗?

【问题讨论】:

  • 我意识到我忘记关闭光标cursor.close()

标签: python mongodb pymongo


【解决方案1】:

您收到此错误是因为光标在服务器上超时(在 10 分钟不活动后)。

来自 pymongo 文档:

如果 MongoDB 中的游标已打开,则它们可能会在服务器上超时 很长一段时间没有对其进行任何操作。这个可以 导致尝试时引发 CursorNotFound 异常 迭代光标。

当您调用collection.find 方法时,它会查询一个集合并将一个游标返回到文档。要获取文档,您需要迭代光标。当您遍历游标时,驱动程序实际上是在向 MongoDB 服务器发出请求以从服务器获取更多数据。每个请求返回的数据量由batch_size()方法设置。

来自documentation

限制一批中返回的文档数量。每批 需要往返服务器。可以调整优化 性能和限制数据传输。

将 batch_size 设置为较低的值将帮助您解决超时错误错误,但它会增加您访问 MongoDB 服务器以获取所有文档的次数。

默认批量大小:

对于大多数查询,第一批返回 101 个文档或刚好够用 文件超过 1 兆字节。批处理大小不会超过最大 BSON 文档大小 (16 MB)。

没有通用的“正确”批量大小。您应该使用不同的值进行测试,看看适合您的用例的值是多少,即您可以在 10 分钟的窗口内处理多少个文档。

最后的手段是设置no_cursor_timeout=True。但是你需要确保在完成数据处理后光标是关闭的。

没有try/except如何避免:

cursor = collection.find(
     {"x": 1},
     no_cursor_timeout=True
)
for doc in cursor:
    # do something with doc
cursor.close()

【讨论】:

  • 我将 batch_size 定义为 50。但是我得到了相同的错误 pymongo.errors.CursorNotFound: cursor id '' 在服务器上无效。我必须设置 batch_size 的正确值是多少?
  • 如何确定使用超时。如何确定光标已关闭?
  • 有一个close 方法可以在关闭光标的光标上调用。
  • 一个相关的问题是聚合是否有超时设置:stackoverflow.com/questions/40248820/…
  • 感谢您写出代码。我以前的所有代码都是内联的,“for doc in collection.find({}):”,你让我看看如何将光标作为变量然后使用并关闭它。
【解决方案2】:

您可以像这样使用no_cursor_timeout=True 使光标不超时:

cursor=db.images.find({}, {'id':1, 'image_path':1, '_id':0}, no_cursor_timeout=True)
for i in cursor:
    # .....
    # .....
cursor.close() # use this or cursor keeps waiting so ur resources are used up

之前这被称为timeout,现在是replaced as per the docs. 有关哪些方法支持 no_cursor_timeout refer this search results in pymongo docs 的更多选项。

【讨论】:

  • 使用no_cursor_timeout=True 后光标仍然崩溃。有什么理由吗?我必须同时使用no_cursor_timeoutbatch_size。我收集了大约 3000 万条记录。
【解决方案3】:

您使用光标的时间超过了超时时间(大约 10 分钟),因此光标不再存在。

您应该选择较低的 batch_size 值来解决问题:

(以 Pymongo 为例)

col.find({}).batch_size(10)

将超时设置为 false col.find(timeout=False),最后不要忘记关闭光标。

【讨论】:

    【解决方案4】:

    这是一个超时问题,在 mongodb 中默认为 10 分钟。 我更喜欢通过登录 mongo 并运行管理查询更新来解决此问题:

    use admin 
    db.runCommand({setParameter:1, cursorTimeoutMillis: 1800000})
    

    其中 1800000 相当于 30 分钟,对于我的用例来说已经足够了。

    或在终端(10800000==3h):

    sudo mongod --setParameter cursorTimeoutMillis=10800000
    

    【讨论】:

      【解决方案5】:

      find 方法中的batch_size 设置为较小的数字。数字是返回记录的数量。这些记录的处理时间应超过 10 分钟(默认服务器光标超时)。否则游标将在服务器上关闭。
      因此,应使用 next 找到适合 batch_size 的值:

      collection.find({...}, batch_size=20)
      

      【讨论】:

        【解决方案6】:

        您可以将光标对象转换为列表然后使用它,这样您就不会再从该光标进行调用,而是来自本地列表。因此,您的代码在该游标上执行这些操作所花费的时间远远高于将游标复制到列表中。因此,它在复制到列表时超时的可能性非常低。因此,一旦完成,它会在特定时间后超时,但无论如何您不再引用它,您将使用自己的列表。

        Cursor = collection.find({ "$and" : [{ "followers" : { "$gt" : 2000 } }, { "followers" : { "$lt" : 3000 } }, { "list_followers" : { "$exists" : False } }] })
        Cursor = [x for x in Cursor]
        

        现在对这个列表做任何事情,你已经获取了其中的所有记录。

        例如 -

        for i in Cursor:
            print(i['screenname'])
        

        【讨论】:

        • 我在大约 650000 条记录的游标中体验到这一点。将其加载到列表中可能行不通。
        猜你喜欢
        • 2018-11-18
        • 2015-06-21
        • 2021-12-05
        • 1970-01-01
        • 2018-04-02
        • 2023-03-04
        • 2012-01-25
        • 1970-01-01
        • 2013-01-25
        相关资源
        最近更新 更多