【问题标题】:Google appengine: Task queue performanceGoogle appengine:任务队列性能
【发布时间】:2017-04-17 18:27:55
【问题描述】:

我目前有一个在 appengine 上运行的应用程序,我正在使用延迟库执行一些作业,其中一些任务每天运行,而其中一些任务每月执行一次。这些任务中的大多数查询 Datastore 以检索文档,然后将实体存储在索引中(搜索 API)。其中一些表每月更换一次,我必须在所有实体(4~5M)上运行这些任务。

此类任务的一个示例是:

def addCompaniesToIndex(cursor=None, n_entities=0, mindate=None):
    #get index
    BATCH_SIZE = 200
    cps, next_cursor, more = Company.query().\
                                     fetch_page(BATCH_SIZE,
                                                start_cursor=cursor)

    doc_list = []

    for i in range(0, len(cps)):
        cp = cps[i]
        #create a Index Document using the Datastore entity
        #this document has only about 5 text fields and one date field
        cp_doc = getCompanyDocument(cp)
        doc_list.append(cp_doc)

    index = search.Index(name='Company')
    index.put(doc_list)

    n_entities += len(doc_list)

    if more:
        logging.debug('Company: %d added to index', n_entities)
        #to_put[:] = []
        doc_list[:] = []
        deferred.defer(addCompaniesToIndex,
                       cursor=next_cursor,
                       n_entities=n_entities,
                       mindate=mindate)
    else:
        logging.debug('Finished Company index creation (%d processed)', n_entities)

当我只运行一项任务时,每个延迟任务的执行大约需要 4-5 秒,因此索引我的 500 万个实体大约需要 35 小时。

另一件事是,当我使用同一队列上的不同延迟任务对另一个索引(例如,每日更新之一)运行更新时,两者的执行速度都会慢很多。并且每次延迟通话开始花费大约 10-15 秒,这简直难以忍受。

我的问题是:有没有办法更快地做到这一点并将推送队列扩展到每次运行的多个作业?或者我应该使用不同的方法来解决这个问题?

提前致谢,

【问题讨论】:

    标签: python google-app-engine app-engine-ndb task-queue


    【解决方案1】:

    通过将if more 语句放在addCompaniesToIndex() 函数的末尾,您实际上是在序列化任务执行:在当前延迟任务完成索引其文档份额之前,不会创建下一个延迟任务。

    您可以做的是将if more 语句移到Company.query().fetch_page() 调用之后,您可以获得(大部分)下一个延迟任务执行所需的变量。

    这样,下一个延迟任务将在当前任务完成之前创建并排队(长时间),因此它们的处理可能会重叠/交错。您还需要进行一些其他修改,例如处理 n_entities 变量,该变量在更新后的场景中失去了当前含义 - 但这或多或少是装饰性的/信息性的,对于实际的文档索引操作不是必需的。

    如果延迟任务的数量非常多,则存在同时排队过多的风险,这可能会导致 GAE 产生的处理它们的实例数量“爆炸式增长”。在这种情况下是不希望的,您可以通过稍微延迟执行来“限制”延迟任务的生成速度,请参阅https://stackoverflow.com/a/38958475/4495081

    【讨论】:

    • 嗨 Dan,我将您的想法应用于我的代码,但我的印象是从 Datastore 读取实际上比在索引中插入这些实体要昂贵得多,因此吞吐量增益不如正如我所期待的那样,我认为最小化读取操作的大小可能会有所帮助,会进行更多测试并回复您。
    • 在这种情况下,最好执行 keys_only 查询,然后在将下一个任务排入队列后,组装页面的键列表并为它们执行批量读取以获取文档并更新索引.
    • 顺便说一句-您实际上可以检查您对数据存储读取成本的怀疑:在开发控制台中检查您的应用程序的日志-某些日志条目在请求持续时间列中有蓝色链接-单击链接,然后在弹出菜单中“查看跟踪”,您将在 StackDriver 中看到类似 appstats 的跟踪,因此您可以更好地了解该特定请求所花费的时间。
    • 谢谢 Dan,我刚刚做了时间跟踪,但得到了这个:imgur.com/ZLmkBm0 这是否意味着该任务大部分时间都是空闲的?
    • 我认为可能与这个问题有关:stackoverflow.com/questions/25796142/…
    【解决方案2】:

    我想我终于设法通过使用两个队列和上一个答案提出的想法来解决这个问题。

    • 在第一个队列中,我们只查询主要实体(使用 keys_only)。并在第二个队列上为这些键启动另一个任务。然后,第一个任务将使用 next_cursorqueue 1 上重新启动。
    • 第二个队列获取实体键并在全文搜索/BigQuery/PubSub 上执行所有查询和插入操作。 (这很慢,每组 100 个键需要 15 秒)

    我也尝试只使用一个队列,但处理吞吐量不太好。我相信这可能是因为我们在同一个队列和调度程序 might not work as well in this case 上运行了慢速和快速任务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-25
      • 2017-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多