pymongo光标'触摸'避免超时

时间:2016-11-04 21:13:21

标签: mongodb pymongo

我需要从mongo(v3.2.10)集合中获取大量(例如1亿)文档(使用Pymongo 3.3.0)并迭代它们。迭代将花费几天时间,由于光标超时,我经常会遇到异常。

在我的情况下,当我迭代时,我需要睡眠不可预测的时间。例如,我可能需要: - 获取10个文档 - 睡1秒钟 - 获取1000个文档 - 睡4个小时 - 获取1个文档 等

我知道我可以:

  • 完全禁用超时,但我想尽可能避免这种情况,因为如果我的代码完全无法正常运行,那么为我清理游标会很好
  • 减少我的光标的batch_size但是如果例如这不会有帮助我需要像上面的例子一样睡4个小时

似乎一个很好的解决方案是一种触摸'光标让它保持活着。因此,例如,我将长睡眠分成较短的间隔,并在每个间隔之间触摸光标。

我没有看到通过pymongo做到这一点的方法,但我想知道是否有人确定它是否可能。

2 个答案:

答案 0 :(得分:3)

当然,这是不可能的,你想要的是功能SERVER-6036,它是未实现的。

对于这样一个长期运行的任务,我建议对索引字段进行查询。例如。如果您的文件都有时间戳“ts”:

documents = list(collection.find().sort('ts').limit(1000))
for doc in documents:
    # ... process doc ...

while True:
    ids = set(doc['_id'] for doc in documents)
    cursor = collection.find({'ts': {'$gte': documents[-1]['ts']}})
    documents = list(cursor.limit(1000).sort('ts'))
    if not documents:
        break  # All done.
    for doc in documents:
        # Avoid overlaps
        if doc['_id'] not in ids:
            # ... process doc ...

此代码完全迭代光标,因此它不会超时,然后处理1000个文档,然后重复下一个1000.

第二个想法:使用a very long cursor timeout配置您的服务器:

mongod --setParameter cursorTimeoutMillis=21600000  # 6 hrs

第三个想法:虽然完全确定,但您可以更确定地通过在with语句中使用它来关闭非超时游标:

cursor = collection.find(..., no_cursor_timeout=True)
with cursor:
    # PyMongo will try to kill cursor on server
    # if you leave this block.
    for doc in cursor:
        # do stuff....

答案 1 :(得分:0)

对我来说,no_cursor_timeout=True都不起作用,因此我创建了一个函数,该函数将游标中的数据保存到一个临时文件中,然后将文件从该文件中返回给调用者。

from tempfile import NamedTemporaryFile
import pickle
import os

def safely_read_from_cursor(cursor):
    # save data in a local file
    with NamedTemporaryFile(suffix='.pickle', prefix='data_', delete=False) as data_file, cursor:
        for count, doc in enumerate(cursor, 1):
            pickle.dump(doc, data_file)

    # open file again and iterate over data
    with open(data_file.name, mode="rb") as data_file:
        for _ in range(count):
            yield pickle.load(data_file)

    # remove temporary file
    os.remove(data_file.name)