谈论并发PHP的最佳方法

问题描述

我正在研究PHP + MysqL项目,我们在其中使用了一个balance列,该列用于跟踪用户的余额。每当用户进行购买时,其余额就会被扣除。但是,我试图找出最好的方法是在尝试购买产品时处理来自同一用户的多个请求。 我已经做过一些研究,并写了以下两种方法,我看不出有什么区别,没有人能解释这两种方法间的区别以及哪种方法在并发方面最好。

Approach 1:
START transaction
"SELECT balance from users where user_id = 1 FOR UPDATE"

check if balance - product price is enough then UPDATE
commit

Approach 2:
"UPDATE users set balance = balance - 30 where user_id = 1 AND balance - 30 >= 0"

如您所见,选项2的代码更少,但是我仍然看到很多人推荐第一种方法(先锁定行然后更新)。

有人可以帮助我了解这里的实际区别是什么,当您关心并发并希望避免多个请求可能使余额列无效时,最好使用哪种区别?如果您有更好的方法,请让我知道,也许我是在考虑这种情况。我正在使用PDO。

解决方法

任何一种解决方案都将最终阻止比赛状况。 SQL数据库具有原子性,一致性,隔离性和持久性(ACID)属性。关于SQL数据库的很酷的事情:如果正确进行事务处理,您不必担心争用条件。

第二个示例中的UPDATE之类的单语句操作始终是原子事务。而且,第一个示例(正确编写)中的显式事务也是原子事务。

就像@GMB一样,我更喜欢尽可能使用单语句操作。但这仅仅是因为代码对于下一个程序员(或我未来的自我)而言更容易理解。两种方法都行。

并且,如果您的业务规则变得更加复杂,则可能需要多语句事务。这是您的第一种方法的优点。

您的解决方案都保留ACID。

,

第二种方法是正确的方法。这是一个查询,同时检查用户是否有足够的发现,并更新其余额。

与第一种方法相反,数据库将在后台正确处理此查询的并发性,第一种方法要求使用事务来避免竞争条件。该行在更新期间被锁定:如果多个会话尝试更新同一行,则update会顺序执行,并且每个查询执行的更改都会影响以下查询。

通常从应用程序中触发查询,然后检查行是否受到影响。如果是,则您知道更新已执行(因此用户有足够的发现)-否则,该事务被拒绝(原因是用户不存在,或者因为它没有足够的发现)。