Multithreading Google数据存储中带游标的多线程处理
我想从Google数据存储加载大量数据 因此,步骤1:我运行查询(使用keysOnly=true)并循环遍历游标,以便每个游标都指向包含600个对象的页面的开头。我将游标存储在局部变量中 步骤2:我为每个游标派生一个线程,在每个线程中加载和处理600个对象 这不是使用游标的通常方式 然而,在我看来这是正确的。步骤1和步骤2中的实际查询字符串是相同的。这类似于通常的无状态web用例,用户可以请求下一页、返回页,然后重新加载上一页;游标不需要直接来自上一个游标查询的结果 我不想为了并行处理给定游标查询中加载的对象而依次遍历游标,然后派生线程,因为我想并行处理来自DB的实际IO密集型查询Multithreading Google数据存储中带游标的多线程处理,multithreading,google-app-engine,google-cloud-datastore,cursors,Multithreading,Google App Engine,Google Cloud Datastore,Cursors,我想从Google数据存储加载大量数据 因此,步骤1:我运行查询(使用keysOnly=true)并循环遍历游标,以便每个游标都指向包含600个对象的页面的开头。我将游标存储在局部变量中 步骤2:我为每个游标派生一个线程,在每个线程中加载和处理600个对象 这不是使用游标的通常方式 然而,在我看来这是正确的。步骤1和步骤2中的实际查询字符串是相同的。这类似于通常的无状态web用例,用户可以请求下一页、返回页,然后重新加载上一页;游标不需要直接来自上一个游标查询的结果 我不想为了并行处理给定游标查
我得到了一些不一致的结果,似乎涉及遗漏的页面和重复加载的对象。这是多线程从Google数据存储加载大量数据的正确方法吗?如果不是,那是什么?我会推荐一种不同的方法。只运行一个循环遍历所有实体的查询。它发生得非常快(不要忘记将批量大小设置为500,默认值仅为10)。如果查询很大,您可能仍然需要使用游标 对于每个实体,使用任务API创建一个任务,并将其添加到任务队列中。这些任务可以并行执行。您可以设置队列上的所有参数
使用这种方法,您不必担心线程,您可以将任务设置为在失败时自动重试,等等。我发现这是应用程序引擎吸引力的一个非常重要的部分-只编写您自己的逻辑,让应用程序引擎担心执行部分。我建议使用另一种方法。只运行一个循环遍历所有实体的查询。它发生得非常快(不要忘记将批量大小设置为500,默认值仅为10)。如果查询很大,您可能仍然需要使用游标 对于每个实体,使用任务API创建一个任务,并将其添加到任务队列中。这些任务可以并行执行。您可以设置队列上的所有参数
使用这种方法,您不必担心线程,您可以将任务设置为在失败时自动重试,等等。我发现这是应用程序引擎吸引力的一个非常重要的部分-只编写您自己的逻辑,让应用程序引擎担心执行部分。根据您的操作,您有几个选项:
max并发请求控制的。如果您的请求可能会失败,并且无法恢复,那么这可能会非常昂贵
根据您所做的工作,您有几个选择:
max并发请求控制的。如果您的请求可能会失败,并且无法恢复,那么这可能会非常昂贵
埃德·戴维森是一名谷歌工程师,负责谷歌数据存储客户端API。他提供了问题的根本原因和建议的解决方案 他说: “查询返回的游标仅在同一查询中有效。当您从[我的步骤1,JF]中的仅键查询切换到[我的步骤2,JF]中的仅非键查询时,游标不再适用…”
“如果您的目标是将结果集拆分为大小相似的块,那么您可能想看看[现在是版本1beta3,JF]。”Ed Davisson,一位从事谷歌数据存储客户端API工作的谷歌工程师。他提供了问题的根本原因和建议的解决方案 他说: “查询返回的游标仅在同一查询中有效。当您从仅键查询[在我的步骤1中,JF]切换到非仅键查询[在我的步骤2中,JF]时,光标不再适用
“如果您的目标是将结果集拆分为大小相似的块,那么您可能需要查看[现在是版本1beta3,JF]。”谢谢,这可能会奏效。但缓慢的部分是实际的查询,而不是每个实体的后期处理