MongoDB游标ID无效错误

43

我尝试遍历这个循环:

for doc in coll.find()

我在处理第100,000条及以上记录时遇到以下错误。

File "build\bdist.win32\egg\pymongo\cursor.py", line 703, in next
File "build\bdist.win32\egg\pymongo\cursor.py", line 679, in _refresh
File "build\bdist.win32\egg\pymongo\cursor.py", line 628, in __send_message
File "build\bdist.win32\egg\pymongo\helpers.py", line 95, in _unpack_response
pymongo.errors.OperationFailure: cursor id '1236484850793' not valid at server

这个错误是什么意思?


4
可能是[MongoDB-错误:getMore命令失败:未找到游标](https://dev59.com/31cP5IYBdhLWcg3wiqZr)的重复问题。 - vovchisko
这是先前的技术,即早期的QA。 - wp78de
5个回答

38

2
FAQ建议您正确理解:http://api.mongodb.org/python/current/faq.html#what-does-operationfailure-cursor-id-not-valid-at-server-mean - Steven Rumbalski
1
请注意,直到您明确关闭它,这将在服务器上消耗内存。请参阅http://www.mongodb.org/display/DOCS/Queries+and+Cursors#QueriesandCursors-ClosingandTimeouts。 - Craig Younkins
@CraigYounkins 如果我说错了,请纠正我,但是使用pymongo时,我相信当游标对象被垃圾收集时就会被关闭,因此除非出现诸如网络故障(这样pymongo就无法告诉服务器关闭它)之类的问题,否则这并不是一个问题。 - drevicko
@drevicko 抱歉,但我不知道。 - Craig Younkins
1
我刚在 pymongo 源代码中查了一下:是的,当游标被垃圾回收时会被关闭。但是网络故障和垃圾回收出现问题(例如:Python 析构函数中的循环引用)可能会阻止它正常工作。 - drevicko
显示剩余2条评论

35
  • timeout=False设置是危险的,永远不应该使用,因为光标与连接可以无限期保持打开状态,这将影响系统性能。文档明确提到需要手动关闭光标。
  • batch_size设置为较小的数字会起作用,但会创建延迟问题,因为我们需要比所需更频繁地访问数据库。
    例如:
    对于带有小批量的5M个文档,检索相同数据需要几个小时,而默认的batch_size返回几分钟内的相同数据。

在我的解决方案中,强制在光标上使用sort

done = False
skip = 0
while not done:
    cursor = coll.find()
    cursor.sort( indexed_parameter ) # recommended to use time or other sequential parameter.
    cursor.skip( skip )
    try:
        for doc in cursor:
            skip += 1
            do_something()
        done = True
    except pymongo.errors.OperationFailure, e:
        msg = e.message
        if not (msg.startswith("cursor id") and msg.endswith("not valid at server")):
            raise

这是一个不错的解决方案,但如果你有几百万条目而且没有“时间或其他顺序参数”,那么它就不切实际了。真不敢相信没有现成的解决方案。 - Mario Alemi
1
只是为了明确。这个解决方案(无论是批处理还是其他)不能保证仅迭代所有文档一次。如果数据库在查询之间更新,某些文档可能会被多次产生或跳过。对于统计目的,这通常不是问题,但如果您需要精确性,则在某些情况下可能会出现问题。 - David Mabodo
“timeout=False是危险的,不应该使用”这是不正确的 - 如果我有一个脚本,逐个迭代集合元素,并且每个元素都需要花费5秒至15分钟的时间,则应该使用此选项。没有游标残留的风险,因为它将随着脚本结束而与集合一起被销毁。 - Maciej Urbański
@MaciejUrbański 根据Mongo 2.4/2.6文档,这是正确的。该参数已更改,甚至名称和功能也可能更改。问题没有标记Mongo版本,因此有点令人困惑。无论如何,保持游标处于打开状态是危险的,由于可能会使用大量内存,如果您打开许多游标并忘记关闭它们。将此选项视为高级用户功能,通常不是您想要执行的操作 - 虽然总有例外情况。感谢您的反馈 :) - Oran

26

timeout=False设定是一个非常不好的做法。更好的方法是估计您的循环在10分钟内可以处理多少个文档,并提出保守的批量大小。这样,MongoDB客户端(在本例中为PyMongo)将不时地查询服务器,每当前一批文档被使用完毕时。这将使游标在服务器上保持活动状态,并且您仍将受到10分钟超时保护的覆盖。

以下是设置游标批量大小的方法:

for doc in coll.find().batch_size(30):
    do_time_consuming_things()

我同意,这听起来是更好的解决方案。 - Tom Swifty

0

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

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

请查看以下答案

-1

你也可以通过以下方式强制进行评估:

for doc in list(coll.find())

@peterh 这个问题是关于修复光标超时问题,而不是解释光标和批处理的工作原理。我同意更详细的解释会更好,但这个答案仍然有效,因为将 cursor 转换为 list 将强制它检索所有批次并关闭,很可能在默认的10分钟过期时间之前。 - Danziger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接