【问题标题】:How to avoid double processing of data on a busy site?如何避免在繁忙的站点上对数据进行双重处理?
【发布时间】:2011-01-26 20:42:02
【问题描述】:

在我的网站上,人们可以将项目添加到他们的愿望清单中。当 X 人将其添加到他们的列表中时,所有这些人的信用卡都会被扣款。

我面临的问题是如何确保如果两个客户同时将其添加到他们的愿望清单中,那么支付处理代码不会运行两次。有什么想法吗?

可能发生的一个例子是:

  • 我们正在等待 20 人将商品添加到他们的愿望清单中,而我们有 19 人。
  • Bob 和 Sally 访问该网站并点击“添加到愿望清单”按钮
  • 服务器收到 Bob 的请求,发现现在有 20 个请求得到满足,然后收取费用。
  • 在服务器收到 Sally 的请求的同时,由于同时收到 Bob 的订单,在 db 中仍然看到 19 个请求,开始处理付款。因此,付款被收取两次。

关于如何避免这种情况的任何想法?

我正在使用 MySQL 数据库和 PHP 进行编程。

【问题讨论】:

  • 你熟悉“交易”吗?
  • 你为什么要因为我把东西放在愿望清单上而向我收费?如果你那样做,我会很生气的!
  • @Hlgem 客户知道他们会被收费,他们会被要求提供付款细节等。别担心 :)

标签: database scalability


【解决方案1】:

这是交易设计的对象类型。卡的收费和 wislist 计数的重置必须在同一个事务中,以便它们作为一个原子单元发生。此外,为避免您描述的问题,您必须将事务隔离级别至少设置为 "Read Committed" "Repeatable Read"。

附加信息:

操作方法如下: 1. 应用程序在数据库上打开一个事务。 2. 应用程序在愿望表上进行选择以检索计数。 3. 如果计数 >= n,则应用程序在愿望清单和相关表格上再次选择以检索待处理的愿望清单订单、用户、卡信息等。 4. 根据有关卡交易的业务规则,应用程序然后删除挂单,或任何将愿望清单计数重置为零的东西。 5. 然后应用程序关闭交易。

它的工作原理如下:当应用对愿望清单表进行选择以检索事务中的计数时,数据库会在与此查询关联的表上放置一个读锁。如果在前一个事务挂起期间打开的另一个事务试图读取这些相同的表,它必须等到前一个事务具有 COMMIT 或 ROLLBACK。如果前一个事务提交,那么下一个事务将看到计数为 0 和所有其他修改。否则,如果应用出于任何原因执行 ROLLBACK,则不会更改任何数据,并且下一个事务会看到第一个事务之前存在的数据。

【讨论】:

  • 如何通过看起来像 MySQL 的事务来完成对卡的收费?
  • 数据库不做刷卡收费。我会发布一个编辑。
  • 有没有办法只锁定与订单相关的特定记录而不是整个表?这样其他所有订单都不会受到影响
  • 这称为锁定粒度,是的,通常可以锁定特定记录。这称为行级锁定。能不能做取决于数据库版本和存储引擎。在 MySQL 中,您应该使用 InnoDB 存储引擎。你在用什么?
【解决方案2】:

我现在正在做一个类似的网站。好像很受欢迎……

您的流程是幂等的,这一点很重要。在这种情况下,这意味着如果您多次运行我们的计费服务,已经计费的订单不会被计费两次。

我通过在下订单时将 OrderStatus 设置为“NotProcessed”来实现这一点。 一旦服务运行并对订单收费,OrderStatus 将更改为“PaymentPending”。 我仅在 OrderStatus 为“未处理”时才对订单收费。

伪代码:

void ProcessPendingOrders()
{
   var orders = getAllOrders();
   foreach(Order order in orders)
   {
    if (order.OrderStatus == NotProcessed)
       ChargeOrder(order)
    }
}

【讨论】:

  • 仍然不能 100% 保证工作,如果进程并行运行,他们可以同时将付款状态设置为待处理,这仍然会导致双重付款。正如其中一位评论者提到的那样,这需要交易或类似的东西..
  • +1 因为提到幂等 w/细节。这是构建的答案。但也必须指出,计费必须由一个辅助进程完成,不应该包含在用户保存收藏夹的交易中。一个辅助进程检查运行批处理的条件。
  • 这取决于您的架构。我使用队列服务,所以每当我需要运行上述服务时,我只需将其推送到队列中即可。我不会运行这样的过程来响应客户端操作 (addToWishlist),而是将消息添加到队列中以便可以正确处理。事务是原子的(全有或全无)。使用数据库,您可以轻松地回滚更改,但使用在线支付则要复杂得多(您愿意回滚支付吗?)。我不会走那条路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-06
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
  • 2013-12-05
  • 1970-01-01
  • 2017-07-03
相关资源
最近更新 更多