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

Should I Use A Queueing System To Handle Payments?

我正在使用 Slim in conjunction with Stripe's PHP Library 在我的应用程序中处理付款。

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

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

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

1) 使用 Queueing System,根据我的理解,它将对这些请求进行排队并一次处理一个,从而创建一种先到先得的排序方式。

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

话虽如此,这些选项有 suggestions/opinions 吗?显然可以完全抛弃我的想法并为我指出不同的方向。

所以如果我理解主要问题是你害怕有人购买已经售出的产品(此时正在处理付款)...

我认为你应该放弃这个排队系统的想法——因为这不是这里的线索。线索是您的商店逻辑。

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

我听过很多关于开发人员使用排队系统的恐怖故事,所以我绝对不建议使用它。先到先得的原则通常会导致在代码到位之前很难发现的错误——我认识的一位开发人员使用类似的系统创建了一家商店,结果发现许多顾客最终得到的是随机物品在他们的购物车中或付款结束时发现没有库存。

相反,我会使用 "event-listener" 使用 jQuery 或 Javascript 或 PHP 库来确保在结帐过程中如果数据库中的某些内容发生更改,则UI 会向用户表明库存不同或不可用。

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

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

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

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

您必须确保按照以下步骤进行并发付款

  1. 您应该在用户即将购买之前检查可用库存 进入支付流程。您可以通过将这些数据保存在 Redis 中而不是每次都查询数据库来做到这一点。每当下订单时,它都会同步更新 Redis 中的库存条目。
  2. 当您要致电支付网关付款时,检查可用库存并在可用时提前处理,否则会提示 用户。
  3. 当用户完成付款时,从数据库事务中的库存中扣除购买数量并使用锁。如果交易不 成功要么退还付款,要么由您决定 是否订购缺货产品。

希望对您有所帮助。

使用 TRANSACTIONS 对此会有好处,因为您似乎遇到了所谓的竞争条件。

摘自 https://www.w3resource.com/mysql/mysql-transaction.php 我引用:

A transaction is a logical unit of work that contains one or more SQL statements. Transactions are atomic units of work that can be committed or rolled back. When a transaction makes multiple changes to the database, either all the changes succeed when the transaction is committed, or all the changes are undone when the transaction is rolled back.

A transaction begins with the first executable SQL statement. A transaction ends when it is committed or rolled back, either explicitly with a COMMIT or ROLLBACK statement or implicitly when a DDL (Data Definition Language (DDL) is used to manage table and index structure and CREATE, ALTER, RENAME, DROP and TRUNCATE statements are to name a few data definition elements) statement is issued.

在交易期间锁定 table 将有助于防止竞争条件。

从同一资源中提取:

LOCK TABLES      
  tbl_name [[AS] alias] lock_type      
  [, tbl_name [[AS] alias] lock_type] ...    

lock_type:      
  READ [LOCAL]    
 | [LOW_PRIORITY] WRITE    

UNLOCK TABLES

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

"About transactions", 拉取自 https://www.informit.com/articles/article.aspx?p=29312

A transaction is a sequential group of database manipulation operations, which is performed as if it were one single work unit. In other words, a transaction will never be complete unless each individual operation within the group is successful. If any operation within the transaction fails, the entire transaction will fail.