如何避免Laravel接口处理Flutter并发请求时重复保存相同记录
问题根因
重复写入的核心原因有两个:
- 逻辑漏洞:你当前的Team存在性判断只校验了
user_id,没有关联week_id,就算没有并发问题,用户跨周创建队伍时也会出现逻辑错误,无法为新的活动周创建队伍。 - 并发竞态条件:12个异步请求几乎同时到达后端,判断Team是否存在、写入Team是两个独立的非原子操作,在第一个请求完成Team写入前,后续所有请求的存在性判断都会拿到空结果,全部进入创建分支,最终生成多条重复记录。
解决方案
1. 数据库层加唯一约束(兜底方案,必须配置)
这是最可靠的防线,就算上层逻辑出问题也不会产生脏数据。给teams表添加user_id和week_id的联合唯一索引,执行如下迁移代码:
Schema::table('teams', function (Blueprint $table) { $table->unique(['user_id', 'week_id']); });
如果需要避免同个俱乐部被重复加入同一个队伍,也可以给user_teams表加team_id和club_id的联合唯一索引。
2. 代码层使用原子查询方法替代分离的判断+写入
用Laravel内置的firstOrCreate方法,该方法配合数据库唯一索引可保证操作原子性,直接替换你原有的Team判断+创建逻辑即可:
public function create_team(Request $request) { $user=User::where('username',$request->username)->first(); $week = Week::where('status','active')->first(); $week_id = $week->id; $club = Club::where('club',$request->club)->first(); // 原子查询或创建Team,不需要自己写if判断 $team = Team::firstOrCreate( // 查询条件:同一个用户同一个活动周仅生成一个Team ['user_id' => $user->id, 'week_id' => $week_id], // 新建时需要填充的额外字段 ['team_name' => '', 'season_id' => 1] ); // 直接写入关联的俱乐部信息 UserTeam::create([ 'team_id'=> $team->id, 'club_id'=> $club->id, ]); return response()->json(['message' => 'Succed']); }
3. 高并发场景可选优化:加请求锁
如果接口并发量极高,可以在请求入口加用户级锁,保证同一个用户的创建请求串行执行:
Cache::lock("create_team_{$user->id}", 10)->get(function () use ($user, $week_id, $club) { // 把原有创建逻辑放在锁闭包内执行即可 });
4. 前端逻辑优化(可选)
你当前的Flutter代码是循环发送12个独立请求,可优化为把整批俱乐部数据打包成一个请求发送到后端,后端批量插入user_teams记录,从根源上避免并发问题,还能减少网络请求开销。
内容的提问来源于stack exchange,提问作者Sermed mayi
相关产品推荐
相关产品推荐

