我们有一个api函数,它使用选择查询来检查数据库中的条件,如果是真的,我们只想一次向数据库中插入一些东西,例如,插入完成的数据库。问题是,当我们多次调用此api函数并发竞争条件时,换句话说,假设我们两次调用此函数,第一个请求检查条件为true,然后第二个请求检查条件为true,因此将它们插入数据库。但是我们希望在检查条件时没有其他人可以再次检查它,直到我们插入为止。
我们使用php / Laravel并了解一些方法,例如使用insert into ... select
或使用诸如replace into ...
之类的东西。
$order = Order::find($orderId);
$logRefer = $order->user->logrefer;
if (!is_null($logRefer) && is_null($logRefer->user_turnover_id)) {
$userTurnover = new UserTurnover();
$userTurnover->user_id = $logRefer->referrer_id;
$userTurnover->order_id = $order->id;
$userTurnover->save();
$logRefer->order_id = $order->id;
$logRefer->user_turnover_id = $userTurnover->id;
$logRefer->save();
}
如果找不到logrefer
,请将其和相应的user-turnover
设置一次。我们期望只看到一个与此订单相关的user-turnover
,但是在同时运行多次之后,我们看到已插入多个user-turnover
。
答案 0 :(得分:1)
当操作需要顺序执行时,我通常会利用事务处理,但是我认为在您的情况下,这有点复杂,因为如果函数正在运行,还需要有条件地评估条件。因此,我可以为您提供的想法是,在数据库上有一个用作信号量的变量(另一个表),该变量允许或不对表执行操作(由您设置或取消设置信号量的值所提供的条件) 。我认为,作为一名优秀的程序员,信号量在许多并发函数中很有用。
答案 1 :(得分:1)
即使代码中的某些机制可以防止重复,数据库也应该在预期唯一的列上具有唯一键。
将连接的查询包装到事务中,该事务将失败并在竞赛事件条件下回滚
try {
DB::transaction(function() {
$order = Order::find($orderId);
...
$logRefer->save();
});
} catch (\Illuminate\Database\QueryException $ex) {
Log::error(“failed to write to database”);
}