ndb.query.count() 失败,大型实体的查询截止日期为 60 秒
Posted
技术标签:
【中文标题】ndb.query.count() 失败,大型实体的查询截止日期为 60 秒【英文标题】:ndb.query.count() failed with 60s query deadline on large entities 【发布时间】:2013-02-03 14:41:05 【问题描述】:对于 google 数据存储中的 100k+ 个实体,ndb.query().count() 将在截止日期前取消,即使有索引。我尝试过使用produce_cursors 选项,但只有iter() 或fetch_page() 会返回游标,而count() 不会。
如何计算大型实体?
【问题讨论】:
我在任务队列中遇到了同样的问题。问题不是前端请求的 60 秒DeadlineExceededError
,而是查询本身的 60 秒(ish?)截止日期,_ToDatastoreError(err)... Timeout: The datastore operation timed out, or the data was temporarily unavailable.
好吧,我猜这是 OP 的问题。
【参考方案1】:
要做这么贵的事情,你应该看看Task Queue Python API。 Google App Engine 基于Task Queue API,提供了deferred library,我们可以使用它来简化运行后台任务的整个过程。
以下是如何在应用中使用延迟库的示例:
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 entitites: ##
行。
正如 Guido 在评论中提到的,这也不会扩展:
这仍然无法扩展(尽管它可能会推迟问题)。一个任务 有 10 分钟而不是 1 分钟,所以也许你可以算 10x 许多实体。但它相当昂贵!搜索分片 如果您想正确解决此问题,请计数器(不幸的是,这很多 工作)。
所以你可能想看看best practices for writing scalable applications,尤其是sharding counters。
【讨论】:
嗯。这仍然无法扩展(尽管它可能会推迟问题)。一个任务有 10 分钟而不是 1 分钟,所以也许你可以算出 10 倍的实体。但它相当昂贵!如果您想正确解决这个问题,请搜索分片计数器(不幸的是,这是很多工作)。 @RayYan 我在 Guido 发表评论后更新了我的答案。所以也要考虑到这一点。【参考方案2】:这确实是一个令人沮丧的问题。我最近一直在这方面做一些工作,以获得一些一般的计数统计信息——基本上,满足某些查询的实体数量。 count()
是个好主意,但它受到数据存储区 RPC 超时的阻碍。
如果count()
以某种方式支持游标会很好,这样您就可以在结果集上游标并简单地将结果整数相加,而不是返回一个大的键列表而只是把它们扔掉。使用光标,您可以使用“传递接力棒”延迟方法继续跨越所有 1 分钟/10 分钟边界。使用count()
(相对于fetch(keys_only=True)
),您可以大大减少浪费并有望提高 RPC 调用的速度,例如,使用fetch(keys_only=True)
方法数到 1,000,000 需要惊人的时间 - 代价高昂关于后端的提议。
如果您只需要/想要定期计数统计信息(例如,我在系统中的所有帐户的每日计数,例如国家/地区),则分片计数器的开销很大。
【讨论】:
这里的解决方案是改用db.Query.count
,它确实支持游标。这是数分钟数和数秒数之间的差异,当数以百万计时。使用deferred.defer
存储和转发结果,在两个60s的请求中最多可以计算2M。请注意,即使您使用的是 ndb
映射,您也可以使用 db.Query.count
。【参考方案3】:
最好使用谷歌应用引擎后端。 后端不受用户请求的 60 秒期限和任务的 10 分钟期限的限制,并且可以无限期运行。 请看这里的文档:https://developers.google.com/appengine/docs/java/backends/overview
【讨论】:
谢谢,但即使是后端,也无法避免查询截止日期 60 秒。 这是真的,是数据库 API 超时,而不是您从中调用它的平台。正如我从类似的问题/答案中发现的那样,这是不可避免的。以上是关于ndb.query.count() 失败,大型实体的查询截止日期为 60 秒的主要内容,如果未能解决你的问题,请参考以下文章
实体框架:Count() 在大型 DbSet 和复杂的 WHERE 子句上非常慢