提问人:awendt 提问时间:4/23/2015 更新时间:10/22/2019 访问量:464
如何并行处理大多数作业,但序列化子集?
How can I process most jobs in parallel but serialize a subset?
问:
我们从供应商那里收到对 Web 应用程序的并发回调,我们怀疑这会导致我们丢失更新,因为它们在不同的机器上同时处理。
当且仅当这些调用影响同一用户记录时,我们才需要序列化这些调用的处理。
我的一位同事提出了一个 AWS Kinesis 流,我们在其中使用用户 ID 作为分区键。这个想法是相同的分区键将记录放在同一个分片中。每个分片仅由一个 worker 处理,不会出现并发问题。根据设计,将保证不会并行处理属于同一用户的记录。这个解决方案可以扩展并解决问题,但它至少会让我们倒退一个冲刺。
我们正在努力寻找一种可以更快地部署的解决方案。
到目前为止,我们讨论过的其他解决方案:
- 只需延迟回调的处理,可能会延迟随机时间。在此方案中,多个辅助角色仍可能(尽管可能性较小)同时处理同一用户的作业。
- 任何排队系统都存在缺陷,即我们要么仅限于一个工人,要么冒着并行处理的风险,或者与(1)中概述的相同。
我们在 MySQL 的 Rails 堆栈上,并且更喜欢 AWS 作为我们的解决方案。
有没有比切换到 Kinesis 更快地产生结果的解决方案?
答:
您基本上是在寻找命名的分布式锁,以便可以强制执行串行处理。
如果您在 AWS 中,则可以使用每个 customerId 将记录推送到 DynamoDB。
每次获取要处理的记录时,请执行一致的读取(请参阅此处的并发部分:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/APISummary.html)。
如果存在记录,请将消息添加到其中(一致写入)。让正在处理的进程在完成后执行读取,如果有消息追加到 dynamo 记录,则按顺序处理它们。最后删除记录。
您可能会遇到争用条件,因此您需要执行回退并重试。我不知道你的音量是多少,但 Dynamo 的速度非常快,所以击中这个次数以上的几率很小。如果它失败的次数太多,你可能不得不将内容转储到错误队列中进行清理,但这不太可能。特别是如果您的卷允许您考虑消息处理中的任意延迟等解决方案。
只有一些理论输入:
如果您有技术上独立的回调,则需要一个语义标识符来将它们标记为从属或独立,以及一个确保执行顺序的序列 ID。用户 ID 是不够的。如何确保一个用户的并行 Web 请求的数据库执行顺序正确?
如果具有唯一的事务 ID,则可以应用序列化等隔离级别。但在这种情况下,您也并非对“您的”丢失的更新无懈可击。使用序列化时也会发生这种情况,除非没有序列号(版本)和锁定机制。
如果你的意思是“丢失更新”,请确保你谈论“覆盖未提交的数据”,以避免误解。这将至少使用隔离级别“可重复读取”进行处理。
我假设在您的回调请求中,您有一个字段,用于确定特定用户的这些回调的顺序。否则,序列化就没有意义了。您可以存储 order UserId 的映射 last callback 的 orderId。 现在,每当您处理任务时,您只需检查该用户的最后一个 orderId,如果该任务不是下一个所需的回调,则将其放回队列中。您的系统将完全并行运行,并且还将保持一致性。
您可以使用芹菜来运行任务,使用 rabbitmq 进行排队。
评论