google-app-engine - 提高 ndb 查询大数据的吞吐量

标签 google-app-engine google-cloud-datastore

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

我一般做什么:

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

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

    在 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 查询。

    有什么建议么?

    最佳答案

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

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

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

    现在,由于 Bx 实例,这会产生额外的成本。我们必须微调我们需要的基本实例的类型和数量。

    关于google-app-engine - 提高 ndb 查询大数据的吞吐量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21941954/

    相关文章:

    python - Google App Engine 中的动态 URL

    java - 并发写入 GAE 数据存储实体的不同属性

    java - 谷歌数据存储: check if ANY entity exists of given kind

    google-app-engine - 我们如何远程使用 Google Datastore Objectify?

    python - 在没有符号链接(symbolic link)的情况下跨服务重用数据存储模型

    java - 如何从google plus中的圈子中检索帖子

    java - MySQL 访问被拒绝的 GAE CloudSQL

    google-app-engine - 按键查询数据存储时得到空值

    google-app-engine - Google AppEngine-未捕获的异常“Google_IO_Exception”无法连接

    python - Google+ 登录 - 服务器端流程 - Python - Google App Engine