【问题标题】:How to update 400,000 GAE datastore entities in parallel?如何并行更新 400,000 个 GAE 数据存储实体?
【发布时间】:2014-02-07 21:58:46
【问题描述】:

我有 400,000 个特定类型的实体,我想对每个实体执行一个简单的操作(添加一个属性)。我无法连续处理它们,因为这需要很长时间。我不想使用 MapReduce 库,因为它既复杂又难以承受。

基本上,我想在任务队列上创建 100 个任务,每个任务占用约 4,000 个实体的片段并对每个实体执行此操作。当所有任务并行执行时,希望这不会花费超过几分钟来处理所有 400k 实体。

但是,我不确定如何使用 GAE 查询来执行此操作。我的实体具有由我的应用程序生成的“230498234-com.example”形式的字符串 ID。我希望每个任务基本上都询问数据存储区,例如“请给我实体 #200,000-#204,000”,然后对它们一一进行操作。

这可能吗?如何以这种方式划分数据存储区?

【问题讨论】:

    标签: java google-app-engine parallel-processing google-cloud-datastore


    【解决方案1】:

    这是 MapReduce (https://developers.google.com/appengine/docs/python/dataprocessing/) 的完美工作。一开始可能很难学,但一旦掌握了,你就会爱上它。

    您也可以考虑在下次保存条目时延迟添加该属性,前提是没有该属性与您的查询中具有默认值相同。

    【讨论】:

    • 他说“我不想使用 MapReduce 库,因为它很复杂而且势不可挡。”如果他只是想这样做,那可能是真的。此外,mapreduce 仍然要求您能够有效地并行处理数据。
    • 他对 mapreduce 的看法可能是错误的,我认为这非常适合他正在寻找的东西。您可以控制映射器的数量以及每个并行任务中有多少用户。
    • 当我不得不向已经创建了数百万个实体的模型添加新属性时,使用 default 值拯救了我。非常感谢朋友。
    【解决方案2】:

    读快,写慢。除非您可以执行有效的查询来分割数据(提示:不要使用偏移分页来执行此操作,因为 appengine 会为每个页面将索引一直走到您的页面,而是使用查询游标),让单个后端执行单个查询和将要处理的数据发送到任务队列。例如,每个可以处理 100 个。 这里的优点是您不需要对数据进行分段,也不需要任何复杂的设置,除了启动一个后端来创建任务队列,因为它从单个查询中读取。新的 appengine 模块可能比标准后端实例更容易(因为它们不会随机停止)。

    如果您想让它真正健壮,请使用带有 pagesize = 元素的查询游标来处理每个任务队列,并记住您创建任务队列的最后一个游标。如果后端在完成之前停止再次启动它,它将从停止的地方恢复。

    【讨论】:

    • 比固定光标大小更好的是使用开始和结束光标。这样,如果在范围内添加了一些东西,worker 就不会错过它应该处理的最后一个项目。但是,普通查询只有弱数据一致性,如果这不够,最好通过队列传递键并让工作人员使用 multiget,如下所述。
    【解决方案3】:

    任务主机可以执行查询并将游标(使用“结束游标”)发布到任务队列,每个对应于 1k 个结果,而不是获取结果。请注意,不能保证工作人员在游标上执行时会看到完全相同的查询结果,但这可能已经足够好了。一个有更多保证的替代方法是在任务主机上执行仅键搜索并实际获取结果(键),然后将 1000 个组发布到任务队列。 Worker 可以使用 multiget 来检索具有更强一致性保证的项目。

    【讨论】:

      【解决方案4】:

      我第一次使用 mapreduce 几乎就是这样。我必须为我的图像模型添加一个属性,我是这样做的:

      在 mapreduce.yaml 中

      mapreduce:
      - name: cleanimages
        mapper:
          input_reader: mapreduce.input_readers.DatastoreInputReader
          handler: main.process
          params:
          - name: entity_kind
            default: main.Image
      

      然后你想发生什么,你输入process代码:

      def process(entity):
          entity.small = None
          yield op.db.Put(entity)
      

      在这种情况下,我只是将其中一个变量设置为 None,因为它不再使用,但是您可以将任何您喜欢的代码放在那里,创建一个新属性并像 abose 一样保存实体。您可以在mapreduce site 找到更多信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多