提高ndb查询对大数据的吞吐量



我正试图在GAE应用程序中对存储在数据存储中的数据执行一些数据处理。瓶颈点是查询返回实体的吞吐量,我想知道如何提高查询的性能。

我一般做什么:

  • 所有事情都在任务队列中工作,所以我们有足够的时间(10分钟的截止日期)
  • 我在ndb实体上运行一个查询,以便选择需要处理的实体
  • 当查询返回结果时,我将实体分组为一批,例如1000个,并将它们发送到另一个任务队列进行进一步处理
  • 存储的数据将是大的(比如500K-1M个实体),并且有可能10分钟的最后期限是不够的。因此,当任务到达任务队列截止日期时,我会生成一个新任务。这意味着我需要一个ndb。光标,以便从停止的位置继续查询

问题是查询返回实体的速率。我尝试了几种方法,并观察到以下性能(这对我的应用程序来说太慢了):

在while循环中使用fetch_page()

代码是直接的

while has_more and theres_more_time:
entities, cursor, more = query.fetch_page(1000, ...)
send_to_process_queue(entities)
has_more = more and cursor

使用这种方法,处理10K个实体需要25-30秒。粗略地说,就是每分钟2万个实体。我尝试更改前端实例的页面大小或类;两人的表现都没有什么不同。

对数据进行分段并并行激发多个fetch_page_async()

此方法取自此处(方法C)

整体性能与上述相同。我尝试了不同数量的段(从2到10),以便有2-10个并行的fetch_async()调用。在所有情况下,总时间保持不变。调用的并行fetch_page_async()越多,每个调用完成所需的时间就越长。我还尝试了20次并行回迁,结果变得更糟了。更改页面大小或前置实例类也没有影响。

使用一个Fetch()调用获取所有内容

现在,这是最不合适的方法(如果不是不合适的话),因为实例可能会耗尽内存,而且我没有光标,以防我需要派生到另一个任务(事实上,我甚至没有能力这样做,任务只会超过截止日期)。出于好奇,我试了一下,看看它的表现如何,我观察到了最好的表现!10K个实体需要8-10秒,大约相当于每分钟60K个实体。现在,这大约比fetch_page()快3倍。我想知道为什么会发生这种事。

在单个循环中使用query.iter()

这与第一种方法类似。这将利用查询迭代器的底层生成器,加上我可以从迭代器中获得一个光标,以防我需要生成一个新任务,所以这很适合我。使用查询迭代程序,它在16-18秒内提取了10K个实体,大约每分钟36-40K个实体。迭代器比fetch_page快30%,但比fetch()慢得多。

对于上述所有方法,我尝试了F1和F4前端实例,但数据存储性能没有任何差异。我还尝试更改查询中的batch_size参数,但仍然没有任何更改。

第一个问题是,为什么fetch()、fetch_page()和iter()的行为如此不同,以及如何使fetch_paage()或iter(?另一个关键问题是,这些吞吐量(每分钟20-60K个实体,取决于api调用)是否是我们在GAE中能做到的最好的吞吐量。

我知道MapReduce API,但我认为它不适合我。AFAIK,MapReduce API不支持查询,我不想扫描所有数据存储实体(它的成本太高,速度太慢-查询可能只返回几个结果)。最后,但同样重要的是,我必须坚持GAE。对我来说,使用另一个平台不是一个选择。所以真正的问题是如何优化ndb查询。

有什么建议吗?

如果有人感兴趣,我可以通过重新设计组件来显著提高数据处理的吞吐量-有人建议我更改数据模型,但这是不可能的。

首先,我对数据进行分段,然后在一个单独的任务队列中处理每个数据段。任务,而不是从单个任务中调用多个fetch_page_async(正如我在第一篇文章中所描述的)。最初,GAE仅利用单个Fx实例按顺序处理这些任务。为了实现任务的并行化,我将组件移动到一个特定的GAE模块,并使用基本的缩放,即可寻址的Bx实例。当我为每个数据段的任务排队时,我通过指定"target"选项明确指示哪个基本实例将处理每个任务。

通过这种设计,我能够在4-5秒内(而不是40'-60'!)处理总共20000个实体,使用5个B4实例。

现在,由于Bx实例,这会带来额外的成本。我们将不得不微调我们需要的基本实例的类型和数量。

新的实验性数据处理功能(MapReduce的AppEngine API)可能比较合适。它使用自动分片来执行多个并行工作进程,这可能有帮助,也可能没有帮助(就像另一个链接问题中的方法C)。

您关于"无需扫描所有实体"的评论引发了一种想法,即自定义索引可以帮助您的查询。这可能需要对模式进行更改,以便以不太正常的形式存储数据

从输出的角度设计解决方案——最简单的查询是什么,可以产生所需的结果,然后是什么实体结构来支持这样的查询,然后需要做什么工作来根据当前数据创建和维护这样的实体结构。

最新更新