SQL阻止同时选择同一个字段

Wil*_*lim 5 php mysqli

我想得到最后的余额并从后端更新xxx用户的一些交易..不幸的是,同时,xxx也从前端进行交易,所以当我处理我的查询时,xxx也处理相同的查询,所以它变得相同最后的余额.

这是我的剧本.
假设:xxx最后余额是10000

$transaction = 1000;
$getData = mysqli_fetch_array(mysqli_query($conn,"select balance from tableA where user='xxx'"));
$balance = $getData["balance"] - $transaction; //10000 - 1000 = 9000
mysqli_query($conn,"update tableA set balance='".$balance."' where user='xxx'");
Run Code Online (Sandbox Code Playgroud)

同时用户xxx从前端做交易..

$transaction = 500;
$getData = mysqli_fetch_array(mysqli_query($conn,"select balance from tableA where user='xxx'"));
$balance = $getData["balance"] - $transaction; //10000-500 it should be 9000-500
mysqli_query($conn,"update tableA set balance='".$balance."' where user='xxx'");
Run Code Online (Sandbox Code Playgroud)

我怎样才能先完成查询,然后用户xxx可以处理查询?

kra*_*kov 2

这是可用的方法之一。

您必须InnoDB为您的表使用引擎。InnoDB 支持行锁,因此您无需锁定整个表来更新与给定用户相关的一个 ROW。(表锁会阻止其他INSERT/UPDATE/DELETE操作执行,导致它们必须等待该表锁被释放)。

在 InnoDB 中,您可以ROW LOCK在执行SELECT查询时使用FOR UPDATE. (但在此你必须使用事务来实现LOCK)。当您SELECT ... FOR UPDATE 在事务中执行此操作时,mysql 会锁定您正在选择的给定行,直到提交事务为止。假设您SELECT ... FOR UPDATE在后端对用户条目 XXX 进行查询,同时前端对同一个 XXX 进行相同的查询。执行的第一个查询(来自后端)将锁定数据库中的条目,第二个查询将等待第一个查询完成,这可能会导致前端请求完成时出现一些延迟。

但要使这种情况起作用,您必须将前端和后端查询都放入事务中,并且两个SELECT查询都必须放在FOR UPDATE最后。

所以你的代码将如下所示:

$transaction = 1000;

mysqli_begin_transaction($conn);

$getData = mysqli_fetch_array(mysqli_query($conn,"SELECT balance FROM tableA WHERE user='xxx' FOR UPDATE"));
$balance = $getData["balance"] - $transaction; //10000 - 1000 = 9000
mysqli_query($conn,"UPDATE tableA SET balance='".$balance."' WHERE user='xxx'");

mysqli_commit($conn);
Run Code Online (Sandbox Code Playgroud)

如果这是您的后端代码,则前端代码应该看起来非常相似 - 具有 begin/commit transaction + FOR UPDATE

最好的事情之一FOR UPDATE,如果您需要查询LOCK某行并在给定场景中使用该数据进行一些计算,但同​​时您需要选择同一行的其他查询,并且它们不需要最新的数据该行中的数据,比您可以简单地执行此查询而无需事务,并且FOR UPDATE最终也没有事务。因此,您将有LOCKED行和其他法线SELECTs正在从中读取(当然他们会读取旧信息......在LOCK开始之前存储)。