0

我将 NDB 用于我的应用程序,并使用带有限制和起始光标的 iter() 来遍历任务中的 20,000 个查询结果。很多时候我遇到超时错误。

超时:数据存储操作超时,或数据暂时不可用。

我打电话的方式是这样的:

results = query.iter(limit=20000, start_cursor=cursor, produce_cursors=True)
for item in results:
  process(item)
save_cursor_for_next_time(results.cursor_after().urlsafe())

我可以减少限制,但我认为一个任务可以运行长达 10 分钟。10 分钟应该足以完成 20000 个结果。事实上,如果运行良好,任务可以在大约一分钟内完成。

如果我切换到 fetch() 或 fetch_page(),它们会更有效率并且不太可能遇到超时错误吗?我怀疑 iter() 中有很多开销会导致超时错误。

谢谢。

4

3 回答 3

1

Fetch 并没有真正提高效率,它们都使用相同的机制,除非您预先知道要多少个实体 - 然后 fetch 可以更有效,因为您最终只需要一次往返。

您可以增加 iter 的批量大小,这可以改善事情。请参阅 https://developers.google.com/appengine/docs/python/ndb/queryclass#kwdargs_options

从文档中默认批量大小为 20,这意味着 20,000 个实体有很多批次。

其他可以提供帮助的事情。考虑在处理中使用 map 和/或 map_async,而不是显式调用 process(entity) 阅读https://developers.google.com/appengine/docs/python/ndb/queries#map 还将异步引入您的处理可能意味着改进的并发性。

说了这么多,你应该分析一下,这样你就可以了解时间的使用情况。例如,process由于您在那里所做的事情,您可能会出现延误。

于 2013-07-24T01:02:40.847 回答
0

ndb 还有其他一些事情需要考虑,比如上下文缓存,你需要禁用它。但我也对这些使用了 iter 方法。我还用旧数据库制作了一个 ndb 版本的 mapper api。

这是我的 ndb 映射器 api,它应该解决超时问题和 ndb 缓存并轻松创建这种东西: http://blog.altlimit.com/2013/05/simple-mapper-class-for-ndb-on-app。 html

使用此映射器 api,您可以创建它,或者您也可以改进它。

class NameYourJob(Mapper):

    def init(self):
        self.KIND = YourItemModel
        self.FILTERS = [YourItemModel.send_email == True]

    def map(self, item):
        # here is your process(item)
        # process here
        item.send_email = False
        self.update(item)

# Then run it like this
from google.appengine.ext import deferred
deferred.defer(NameYourJob().run, 50,  # <-- this is your batch 
    _target='backend_name_if_you_want', _name='a_name_to_avoid_dups')
于 2013-07-23T20:40:50.760 回答
0

对于可能较长的查询迭代,我们使用时间检查来确保可以处理缓慢的处理。鉴于 GAE 基础架构性能的差异,您可能永远找不到最佳处理数。下面的代码摘录来自我们使用的一个在线维护处理程序,它通常在十秒内运行。如果没有,我们会得到一个返回码,说明由于我们的计时器检查,它需要再次运行。在您的情况下,您可能会在将光标传递给下一个队列任务后中断该过程。这里有一些示例代码,经过编辑希望能让您对我们的逻辑有一个很好的了解。另一个注意事项:您可以选择将其分解成更小的部分,然后通过重新排队任务直到完成,从而将较小的任务扇出。在 GAE 高度多变的环境中,一次做 20k 件事情似乎非常激进。HTH-步骤

def over_dt_limit(start, milliseconds):
    dt = datetime.datetime.now() - start
    mt = float(dt.seconds * 1000) + (float(dt.microseconds)/float(1000))
    if mt > float(milliseconds):
        return True
    return False


    #set a start time
    start = datetime.datetime.now()
    # handle a timeout issue inside your query iteration
    for item in query.iter():
        # do your loop logic
        if over_dt_limit(start, 9000):
            # your specific time-out logic here
            break
于 2013-07-24T01:52:21.053 回答