You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10.43.0:避免并发请求更新同一条数据行

解决Laravel库存并发分配的问题

你的代码确实存在并发安全问题:first()和update()是两个独立的数据库操作,当多个请求同时进来时,它们可能会同时查到同一条available=true的库存记录,进而导致重复分配。

方案一:使用悲观锁(lockForUpdate)

Laravel的悲观锁需要在事务中使用才能生效,它会锁定选中的行,直到当前事务完成,其他请求必须等待锁释放才能操作该行。修改后的代码如下:

public function assignItemToAccount(Account $account): Inventory
{
    return DB::transaction(function () use ($account) {
        // 锁定第一条可用库存,其他请求需等待当前事务结束
        $item = Inventory::where('available', true)
            ->lockForUpdate()
            ->first();

        if (!$item) {
            throw new \Exception('暂无可用库存');
        }

        $item->update([
            'account_id' => $account->id,
            'available' => false
        ]);

        return $item->fresh();
    });
}

原理:lockForUpdate()会给查询到的行加上排他锁,在当前事务提交或回滚前,其他请求查询这条记录时会被阻塞,从根本上避免了并发抢占。

方案二:原子更新(更高效)

如果你的业务逻辑只是“选一条可用库存并更新”,推荐用原子更新的方式——把查询和更新合并成一个数据库操作,数据库本身会保证这个操作的原子性,不需要额外加锁,性能更优:

public function assignItemToAccount(Account $account): ?Inventory
{
    // 原子操作:找到第一条可用库存并直接更新,返回受影响的行数
    $updatedRows = Inventory::where('available', true)
        ->orderBy('id') // 固定选取顺序,避免随机分配
        ->limit(1)
        ->update([
            'account_id' => $account->id,
            'available' => false
        ]);

    if ($updatedRows === 0) {
        throw new \Exception('暂无可用库存');
    }

    // 获取刚更新的库存记录
    return Inventory::where('account_id', $account->id)
        ->where('available', false)
        ->orderBy('updated_at', 'desc')
        ->first();
}

原理:update()是单个数据库请求,数据库会确保同一时间只有一个请求能更新同一条记录,完全避免了并发冲突,适合高并发场景。

方案对比

  • 悲观锁:适合需要先读取记录、再执行复杂业务逻辑(比如计算、调用其他接口)后再更新的场景,但会带来一定的性能开销(阻塞等待)。
  • 原子更新:适合简单的“选更合一”场景,性能更高,无阻塞。

内容的提问来源于stack exchange,提问作者Vince

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:03:19