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



我试图获取一些存在于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:光标id '…

我找到了这篇关于那个问题的文章。然而,我不清楚该采取哪种解决办法。可以用find().batch_size(30)吗?上面的命令到底是做什么的?我可以使用batch_size获取所有数据库id吗?

你得到这个错误是因为光标在服务器上超时了(在10分钟不活动之后)。

来自pymongo文档:

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

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

来自文档:

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

将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()

您可以像这样使用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,根据文档已被替换。有关支持no_cursor_timeout的方法的更多选项,请参阅pymongo文档中的搜索结果。

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

你应该选择一个较低的batch_size值来解决这个问题:

(以python为例)

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

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

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

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

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

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

sudo mongod --setParameter cursorTimeoutMillis=10800000

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

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

你可以把游标对象转换成一个列表然后使用它,所以你不会再从那个游标发出调用它会从一个本地列表发出。因此,代码在游标上执行这些操作所花费的时间要比仅仅将游标复制到列表中要多得多。所以当它复制到列表时超时的概率非常低。因此,一旦它完成,它会在特定的时间后超时,但无论如何你不再引用它,你将使用你自己的列表。

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

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

for eg -

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

您还应该考虑,如果会话空闲时间超过30分钟,MongoDB服务器将该会话标记为过期,并可能随时关闭它。当MongoDB服务器关闭会话时,它也会杀死任何正在进行的操作,并打开与会话相关的游标。这包括用noCursorTimeout()配置的游标。

您可以通过使用Mongo.startSession()在显式会话中发出操作并使用refreshSessions命令定期刷新会话来解决这个问题。

from pymongo import MongoClient
from datetime import datetime, timedelta
client = MongoClient()
session = client.start_session()
sessionId = session.session_id
print(sessionId)
collection = session.client.examples.data
cursor = collection.find(no_cursor_timeout=True)
refresh_timestamp = datetime.now()
for document in cursor:
    # Check if more than 5 minutes have passed since the last refresh
    if (datetime.now() - refresh_timestamp).total_seconds() > 300:
        print("refreshing session")
        session.client.admin.command({"refreshSessions": [sessionId]})
        refresh_timestamp = datetime.now()
# Continue processing document.

相关内容

最新更新