Google 数据存储中的游标多线程

Multithreading with cursors in Google Datastore

我想从 Google 数据存储加载大量数据。

因此,第 1 步:我 运行 查询(使用 keysOnly=true)并遍历游标,以便每个游标都指向包含 600 个对象的页面的开头。我将游标存储在局部变量中。

第 2 步:我为每个游标分拆一个线程,在每个线程中加载和处理 600 个对象。

这不是通常使用游标的方式。

但是,我认为它是正确的。步骤 1 和步骤 2 中的实际查询字符串是相同的。这类似于通常的无状态 Web 用例,用户可能会要求下一步、返回,然后重新加载上一页;游标不需要直接来自前一个游标查询的结果。

我不想按顺序单步执行游标然后分离线程以并行处理在给定游标查询中加载的对象,因为我想并行化来自数据库的实际 IO 密集型查询.

我发现结果有些不一致,似乎涉及遗漏页面和重复加载对象。这是从 Google 数据存储多线程加载大量数据的正确方法吗?或者如果不是,那是什么?

我会推荐一种不同的方法。 运行 只有一个查询循环遍历您的所有实体。它发生得非常快(不要忘记将批量大小设置为 500,默认值仅为 10)。如果查询很大,您仍然可能需要使用游标。

使用 Task API 为每个实体创建一个任务并将其添加到任务队列中。这些任务可以并行执行。您可以在队列上设置所有参数。

使用这种方法,您不必担心线程,您可以将任务设置为在失败时自动重试等。我发现它是 App Engine 吸引力的一个非常重要的部分 - 只写你自己的逻辑,让 App Engine 去操心执行部分。

根据您的工作,您有以下几种选择:

  1. 如果您有大量数据 - 使用带有任务队列的 'fan out' 模型。在此模型中,任务队列作业加载一段数据,对其进行处理并存储结果,并可能触发更多处理作业。任务队列限制允许您控制 throughput/duration/cost 并处理 failure/retry。此模型的一个优点是您可以通过手动戳 URL 来测试和重新运行 段,并在管理面板中查看进度。

  2. 使用 GAE MapReduce - https://cloud.google.com/appengine/docs/java/dataprocessing/

  3. 如果你有少量数据,在单个进程中。缺点是请求截止时间(60 秒、10 分钟或 24 小时——取决于服务器和请求的类型)。回想一下,数据存储区操作是异步的,因此您可以 运行 在单个线程中并行请求,这可能会简化您的代码。在它们成为阻塞之前有多少(我相信)由您的 appengine-web.xml 或 app.yaml 中的 max-concurrent-requests 控制。如果您的请求可能会失败并且不是可恢复的,这可能会非常昂贵。

Ed Davisson,Google 工程师,负责 Google 数据存储客户端 API、answered this。他提供了问题的根本原因和建议的解决方案。

他说:

"查询返回的游标仅在同一查询中使用有效。当您从仅键查询 [在我的步骤 1 中,JF] 切换到非键查询 [在我的第 2 步,JF],游标不再适用....

"If your goal is to split a result set into similar sized chunks, you might want to take a look at QuerySplitter [which is now in version 1beta3, JF]."