mongodb pymongo.errors.CursorNotFound:游标ID“...”在服务器上无效

khbbv19g  于 2023-05-17  发布在  Go
关注(0)|答案(7)|浏览(319)

我尝试使用以下代码获取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“...”在服务器上无效。
我发现这个article指的是那个问题。然而,我不清楚采取哪种解决办法。可以使用find().batch_size(30)吗?上面的命令到底做了什么?我可以使用batch_size获取所有数据库ID吗?

6ie5vjzr

6ie5vjzr1#

您收到此错误是因为服务器上的游标超时(在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()
ylamdve6

ylamdve62#

您可以通过使用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.

r1zhe5dt

r1zhe5dt3#

您使用光标的时间超过了超时时间(大约10分钟),因此光标不再存在。
你应该选择一个较低的batch_size值来解决这个问题:
(with例如Pymongo)

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


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

8wtpewkr

8wtpewkr4#

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

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

其中1800000相当于30分钟,对于我的用例来说已经足够了。
或在终端(10800000==3h):

sudo mongod --setParameter cursorTimeoutMillis=10800000
b1zrtrql

b1zrtrql5#

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

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

mlnl4t2r6#

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

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'])
5fjcxozz

5fjcxozz7#

您还应该考虑到,如果会话空闲时间超过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.

相关问题