我应该使用排队系统来处理付款吗?



我正在使用 Slim 和 Stripe 的 PHP 库来处理应用程序中的付款。

一切都很好,但是直到最近,我还在我的系统中发现了一个令人震惊的错误,我认为这可能是一个比我想象的要大得多的问题。在我的逻辑中,在付款流程的三个独立检查点,我检查我的(MySQL)数据库中的库存,以确保用户购买的产品不会超过可用产品的数量。

但是,当多个用户在大约500 毫秒内相互发出请求时,支付系统似乎会同时处理这些请求,从而导致一系列问题,从不正确和不平衡的库存到虚假用户成功付款确认。

通过一些尽职调查,我将解决方案缩小到两个选项(尽管我可能会卖空自己):

1)使用排队系统,根据我的理解,它将对这些请求进行排队并一次处理一个,从而创建一个先到先得的原则。

2)将一些中间件附加到每个请求上,这些中间件将充当队列并尝试同步处理每个请求(尽管这可能类似于我已经拥有的请求)

话虽如此,对这些选项有什么建议/意见吗? 显然,您可以完全放弃我的想法,为我指出不同的方向。

因此,如果我了解主要问题是您担心有人购买已经售出的产品(此时正在处理付款)...

我认为你应该离开这个排队系统的想法 - 因为这不是这里的线索。线索是你的商店逻辑。

我不知道您的商店是如何运作的,但从逻辑的角度来看,产品应该在客户发送订单的那一刻锁定(分配),而不是在付款过程之后。因为不是每次付款都可能成功(如果客户想重试不成功的付款,请使用条纹或其他付款方式付款怎么办?

使用 TRANSACTIONS 对此是有益的,因为您似乎面临着所谓的竞争条件。

从 https://www.w3resource.com/mysql/mysql-transaction.php 中提取,我引用:

事务是包含一个或多个 SQL 语句的逻辑工作单元。事务是可以提交或回滚的原子工作单元。当事务对数据库进行多项更改时,要么在提交事务时所有更改都成功,要么在回滚事务时撤消所有更改。

事务以第一个可执行 SQL 语句开头。事务在提交或回滚时结束,无论是使用 COMMIT 或 ROLLBACK 语句显式执行,还是在发出 DDL(数据定义语言 (DDL) 用于管理表和索引结构以及 CREATE、ALTER、RENAME、DROP 和 TRUNCATE 语句仅举几个数据定义元素)语句时隐式结束。

在事务期间锁定表将有助于防止争用条件。

从同一资源拉取:

LOCK TABLES      
tbl_name [[AS] alias] lock_type      
[, tbl_name [[AS] alias] lock_type] ...    
lock_type:      
READ [LOCAL]    
| [LOW_PRIORITY] WRITE    
UNLOCK TABLES

更多信息和语法可以在(官方)MySQL网站上找到:

  • https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-transactions.html
  • https://dev.mysql.com/doc/refman/5.7/en/commit.html

"关于交易",摘自 https://www.informit.com/articles/article.aspx?p=29312

事务是一组连续的数据库操作操作,其执行方式就像一个工作单元一样。换句话说,除非组中的每个操作都成功,否则事务将永远不会完成。如果事务中的任何操作失败,则整个事务将失败。

我听说过很多关于开发人员使用队列系统的恐怖故事,所以我肯定会推荐不要这样做。先到先得的原则通常会导致错误,直到代码到位后才很难发现 - 我认识的一位开发人员使用类似的系统创建了一个商店,却发现许多客户最终在购物车中随机购买商品或付款结束时发现没有商品有库存。

相反,我会使用jQuery或Javascript或PHP库的"事件侦听器"来确保在结帐过程中,如果数据库中的某些内容发生了变化,那么UI将向用户指示股票不同或不可用。

我认为您的问题与您的支付提供商无关。这是你的商店逻辑。我会做以下几点:

  1. 用户点击产品以添加到购物车
  2. 产品进入"结账"列表,商店中的可用数量减少一个
  3. 那么我们有两种可能性:
    • 用户以成功付款完成结帐=>您可以保留产品的总金额
    • 用户中止结帐 =>您必须在特定时间(例如 5 分钟)后增加产品总量

如果您尝试在电影院购买座位,则必须在5分钟内完成结帐(向用户显示倒计时),否则预订将丢失,其他人可以预订座位。

我想建议另一种方法,这是我们处理商店付款和库存的方式。 主要问题出现在大批量销售期间,我们使用库存管理解决方案处理黑色星期五和网络星期一的销售。

您必须确保并发付款的以下步骤

  1. 您应该在用户即将检查可用库存之前 输入付款流程。为此,您可以将此数据保留在 Redis 中,而不是每次都查询数据库。每当下订单时,它都会更新 Redis 中的库存条目以保持同步。
  2. 当您要致电支付网关进行付款时,请检查可用库存并提前处理(如果有),否则会提示 用户。
  3. 当用户完成付款时,从数据库事务中的库存中扣除购买数量并使用锁。 如果交易不是 成功退款或取决于您想要支付的款项 订购缺货产品与否。

希望这对你有帮助。

最新更新