我正在尝试在GAE应用程序中对存储在数据存储区中的数据执行某些数据处理。瓶颈点是查询返回实体的吞吐量,我想知道如何提高查询的性能。
我的一般做法:
问题是查询返回实体的速率。我尝试了几种方法并观察了以下性能(这对我的应用来说太慢了):
在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秒。粗略地说,这是每分钟20K实体。我尝试更改页面大小或前端实例的类;没有任何表现上的差异。
对数据进行细分并同时触发多个fetch_page_async()。
This approach is taken from here (approach 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_page()或iter()与fetch()一样好?然后另一个关键问题是这些吞吐量(每分钟20-60K实体,取决于api调用)是否是GAE中最好的。
我知道MapReduce API,但我认为它不适合我。 AFAIK,MapReduce API不支持查询,我不想扫描所有数据存储区实体(它会太昂贵和缓慢 - 查询可能只返回一些结果)。最后,但并非最不重要的是,我必须坚持GAE。诉诸另一个平台对我来说不是一个选择。所以问题实际上是如何优化ndb查询。
有什么建议吗?
答案 0 :(得分:2)
如果有人感兴趣,我可以通过重新设计组件来显着提高数据处理的吞吐量 - 建议我更改数据模型,但这是不可能的。
首先,我对数据进行分段,然后在单独的taskqueue.Task中处理每个数据段,而不是从单个任务调用多个fetch_page_async(如我在第一篇文章中所述)。最初,GAE按顺序仅使用单个Fx实例处理这些任务。为了实现任务的并行化,我将组件移动到特定的GAE模块并使用基本缩放,即可寻址的Bx实例。当我为每个数据段排队任务时,我通过指定'target'选项明确指示哪个基本实例将处理每个任务。
通过这种设计,我能够在4-5秒内(而不是40'-60'!)总共处理20,000个实体,使用5个B4实例。
现在,由于Bx实例,这会产生额外的成本。我们必须微调我们需要的基本实例的类型和数量。
答案 1 :(得分:0)
新的实验性Data Processing功能(适用于MapReduce的AppEngine API)可能更合适。它使用自动分片来执行多个并行工作进程,这可能有助于也可能没有帮助(如其他链接问题中的方法C)。
答案 2 :(得分:0)
您对“无需扫描所有实体”的评论触发了自定义索引可以帮助您查询的想法。这可能需要进行架构更改,以便以较不正常的形式存储数据。
从输出角度设计解决方案 - 产生所需结果的最简单查询是什么,然后实体结构支持这样的查询,然后从当前创建和维护这样的实体结构需要做些什么工作数据