【问题标题】:Multiple Update statements on a single row at exactly same time同一行上的多个 Update 语句
【发布时间】:2020-02-09 21:21:27
【问题描述】:

我有一个场景,我收到客户的出租车请求,然后我向多个司机发送取车请求。现在想象一下,如果两个收到请求的司机同时点击“接受”按钮,那么哪个司机会搭车。

我有一个骑行表,其中包含ride_id、driver_id、fulfilled(Boolean) 列。

到目前为止,我正在做的是在点击“接受”按钮后立即调用 API。此 GET 请求 API 检查行程是否已完成。如果是,我会向司机显示一​​条消息,说乘车已经完成,否则我会点击另一个 POST API 请求,该请求将 DB 中的已完成值更新为 true 并更新 driver_id。

现在进入我们的场景,当两个驱动程序同时点击“Accept”时,将发出两个 GET 请求,并且两者都将得到“Not Fulfilled”作为响应,此后两者都将发送一个 POST 请求。现在我很困惑谁的数据将在 DB 中更新。

我在后端使用 PHP 和 MYSQL 作为数据库。

【问题讨论】:

  • 数据库应该为您处理这一切。你只需要正确处理失败的更新
  • 同意。但是有什么方法可以确定数据库将首先选择哪个请求?
  • @RiggsFolly 或多或少意味着运行 -> UPDATE ... SET fulfilled = 1 [,...] WHERE fulfilled = 0 AND ride_id = ? 然后顺序无关紧要哪个更新先出现。兜风。
  • @RaymondNijland 是的,我明白了,但也有一个 driver_id 列。那么如何确定哪个司机的 ID 将在那里呢?或者我只能在插入请求后进行检查,然后进行选择调用以检查更新的驱动程序 ID?
  • 查看@GMB 的 SQL 也使用了与我相同的想法,但添加了解释..

标签: php mysql rowlocking table-locking


【解决方案1】:

您正在通过运行SELECT,然后运行UPDATE 创建竞争条件。但是所有数据库都可以非常有效地做的一件事是管理并发性。因此,更简单的解决方案是在驱动程序点击接受按钮时直接运行更新,例如:

UPDATE ride 
SET driver_id = :driver_id, fulfilled = 1 
WHERE ride_id = :ride_id AND fulfilled = 0

然后在您的应用程序中检查记录是否受到查询的影响。如果是,那么这个司机就赢了。如果没有记录受到影响,则意味着之前有其他司机接管了该行程。

【讨论】:

  • 好的,为了确定哪个司机赢得了行程,我必须为ride_id 再做一个选择语句并在那里检查司机的ID?
  • “好的,为了确定哪个司机赢得了行程,我必须为 order_id 再做一个选择语句并在那里检查司机的 ID?” 否 @DeepakSingh,GMB表示运行mysqli_affected_rows() 或在UPDATE 之后执行相同操作的 PDO 函数
  • @RaymondNijland mysqli_affected_rows 未指定受影响的行 (id)。它只是计算实际受影响的行数。在这种情况下,需要在更新过程之后进行显式选择
  • @MadhurBhaiya:在这种情况下,我实际上并不认为需要获取获胜司机的 id。查询来自给定司机的前端,结果告诉他他是否赢了。
  • @GMB ..是的,对。在对这个问题进行二读时,这是有道理的!
【解决方案2】:

两个驱动程序同时按下接受按钮不会转化为同时更新单行。你这种情况发生的可能性非常小。

但如果确实发生了,不一定是两个事务更新一行的情况。这是一个事务尝试更新当前正在更新的行的情况。在任何情况下,当前正在更新该行的事务都会获得对该行的锁定,并且任何其他尝试更新同一行的事务都会排队。

第一个事务完成更新后,它释放锁,队列中其他个事务中的第一个获得锁以执行它自己的更新。这种情况一直持续到等待队列为空。

有趣的是,这个工具已经内置在大多数关系数据库中,因此您不必担心两个事务会同时更新数据库。然而,一个有趣的研究领域是如何在这种场景中管理事务队列(例如,优先考虑等待事务)。

【讨论】:

  • topcistarter 所追求的系统和处理点是 FIFO 获胜系统。在第一个答题器中,很确定你没有正确解释问题。
  • 锁定是我最初的想法。但由于他的前端和后端是解耦的,我看不出这是如何工作的。
  • 确实@GMB,你会锁定它意味着第二次点击或之后的许多其他点击仍会否决第一次点击..
  • @RaymondNijland,您的解决方案有效。但是现代关系数据库采用锁定来防止问题标题暗示的可能
  • “但是现代关系数据库采用锁定来防止问题标题暗示的可能性” 第一次单击 START TRANSACTION UPDATE ride SET driver_id = 1, fulfilled = 1 WHERE ride_id = 1; COMMIT; 并在第二次单击后运行 START TRANSACTION UPDATE ride SET driver_id = 2, fulfilled = 1 WHERE ride_id = 1; COMMIT; 会发生什么?从 MySQL 的角度来看,这就是发生的事情,driver_id 2 使用事务不会改变这一点。除非你改变状态-> fulfilled 并像我建议的那样在WHERE 中使用该条件检查。
猜你喜欢
  • 1970-01-01
  • 2023-03-16
  • 2018-01-07
  • 2020-04-12
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多