1

我看到一个非常常见的要求是更新特定实体类型的所有行的字段或值,它通常超过 10 分钟的队列限制。

那么,使用可以完成所有行更新的任务队列来运行 cron 作业的最佳方法是什么?

我尝试的一种方法是在 cron 作业中触发查询,然后创建多个大小相同的 id 列表,例如每个列表包含 100 个 id。然后通过传递 id 列表为每个列表生成一个任务。然后在任务代码中使用获取实体行

pm.getObjectId 然后处理它。

我仍然觉得这种方法有点手动而不是智能。有没有更好的处理方法?

4

2 回答 2

2

如果您有现金可烧,请使用后端;它们没有限制(尽管使用后端来处理单个大请求是浪费的......只有在您有其他工作可以卸载时才考虑这一点)。

更有可能的是,您真正想做的是分片。也就是说,将一个大的线性任务分解成一堆更小的、可并行化的任务。

我经常使用的常见模式是让一个请求只进行调度......也就是说,查询您需要做的工作,收集要操作的键列表,然后启动批量工作,例如,一次 100 个任务(发送尽可能多的数据,以避免在不需要时重新查询)。

这样,只有 dispatcher 需要导航完整的数据集,而不需要执行任何耗时的更新,而且只要不到 10 分钟,你应该是黄金。

现在,根据您的实体祖先设置,您可能会在尝试并行更新数千个实体时遇到争用(如果您的调度程序太快,可能会发生这种情况)。简单的解决方案是设置 .withCountDownMillis((latency+=1000)) 为每个请求提供大约一秒钟的喘息空间(可能更多,具体取决于实体的大小和每个实体的索引数量)。使用 appstats 对您的应用程序进行基准测试,以查看每个实际需要多长时间,并给它们额外的 500 毫秒左右来覆盖标准偏差。

现在...我还想知道您正在处理多少个实体,那 10 分钟还不够长...您使用的是异步 api 吗?批处理请求怎么样?如果您一次对一个实体进行操作,并阻止每个实体的 get/put,您将很容易达到限制。

相反,请查看异步请求。使用异步,我可以启动一个 put,存储 Future,再启动一堆,然后当我最终确定 Future 时,操作已经完成,我基本上支付 0milli 墙时间阻塞请求。

即使你不能使用低级异步(仍然强烈推荐),至少考虑使用批处理。也就是说,不是一次放一个,而是使用一个列表并每隔 50 个左右的实体执行一次 put + clear(如果它们很小,则更多)。这允许 appengine 内部后端并行化所有 50 个,因此您为每个实体序列化开销支付 1+ 的时间。

将异步和批处理与非争议实体结合起来,我通常能够每分钟处理大约 4000 个实体。如果您必须处理 40,000 多个实体,那么您需要研究适当的分片。为此,每(任意选择)1000 个实体获取一个键,并启动一个从前一个键(或空值)查询到下一个键的任务。这使您可以通过完成一项大工作并将其转变为更小的工作,从而在短时间内运行尽可能多的实体。

于 2013-01-04T11:33:45.947 回答
1

我用它来更新任务队列 10 分钟限制内的数百万条记录:

  1. 创建一个循环,在每次迭代中使用光标运行查询(不要使用offset())。在每次迭代中使用下一个光标。这样,您将有效地遍历整个目标实体范围。用于limit(1000)每次获取一批 1000 个实体。还将预取大小设置为 1000 以最小化网络往返。

  2. 对于每个批次,更新属性,然后执行async put

于 2013-01-05T08:49:01.127 回答