ndb.query.count() 在大实体上超时 60 秒
在谷歌的数据存储中,如果有超过10万个实体,使用ndb.query().count()来计算数量时,可能会因为超时而被取消,即使有索引也不行。我试过使用produce_cursors选项,但只有iter()或fetch_page()能返回游标,而count()却不能。
那么,怎么才能计算大量的实体呢?
3 个回答
使用谷歌应用引擎的后端服务会更好。后端服务不受用户请求的60秒限制,也没有任务的10分钟限制,可以一直运行下去。想了解更多,可以查看这里的文档:https://developers.google.com/appengine/docs/java/backends/overview
这确实是个让人头疼的问题。我最近在这个领域做了一些工作,主要是想获取一些统计数据——也就是满足某个查询条件的实体数量。count()
这个功能听起来不错,但由于数据存储的远程调用超时,它的效果受到了限制。
如果count()
能支持游标就好了,这样你就可以在结果集中逐个处理,简单地把结果加起来,而不是返回一大堆键然后又把它们丢掉。有了游标,你可以跨越所有1分钟或10分钟的时间段,采用“接力”的方式继续处理。使用count()
(而不是fetch(keys_only=True)
)可以大大减少资源浪费,并希望能提高远程调用的速度。例如,使用fetch(keys_only=True)
方法来统计到1,000,000的数量,所花费的时间是非常惊人的,这对后端来说是个很大的负担。
如果你只需要或想要定期的统计数据(比如每天统计一下系统中所有账户的数量,按国家分类),那么分片计数器就显得很繁琐。
如果你想做一些比较复杂的事情,可以看看 任务队列 Python API。基于这个任务队列 API,Google App Engine 提供了 延迟库,我们可以用它来简化后台任务的处理过程。
下面是一个如何在你的应用中使用延迟库的例子:
import logging
def count_large_query(query):
total = query.count()
logging.info('Total entities: %d' % total)
然后你可以在你的应用中这样调用上面的函数:
from google.appengine.ext import deferred
# Somewhere in your request:
deferred.defer(count_large_query, ndb.query())
虽然我还不确定 count()
在这么大的数据存储下会不会返回结果,但你可以试试这个 count_large_query()
函数,它使用了游标(未经测试):
LIMIT = 1024
def count_large_query(query):
cursor = None
more = True
total = 0
while more:
ndbs, cursor, more = query.fetch_page(LIMIT, start_cursor=cursor, keys_only=True)
total += len(ndbs)
logging.info('Total entitites: %d' % total)
如果想在本地测试上面的代码,可以把 LIMIT
设置为 4,然后在你的控制台查看是否能看到 Total entities: ##
这一行。
正如 Guido 在评论中提到的,这种方法也无法扩展:
这仍然无法扩展(虽然可能会暂时缓解问题)。一个任务的时间限制是 10 分钟,而不是 1 分钟,所以你可能可以计算 10 倍的实体。但这代价不小!如果你想好好解决这个问题,可以搜索一下分片计数器(不幸的是,这需要很多工作)。
所以你可能想看看 编写可扩展应用的最佳实践,尤其是 分片计数器。