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 子句上非常慢

数据库大型(> 60 表)的用户代码优先实体框架

GAE ndb 存储大型一对多关系的最佳实践

我可以在不创建实体类的情况下对大型 sql 使用休眠命名查询吗?

SSIS DataReader 从大型机失败