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

Laravel中save()方法数据重复插入问题的排查与解决咨询

推荐记录重复插入问题排查与解决

问题描述

负责实现数据库插入/保存新推荐记录功能,本周在数千条记录中出现3条重复记录。当前数据库未设置唯一约束,想了解现有系统内是否有办法避免该问题,以及可能的根因。已测试双击、慢速网络下双击、断网提交后联网再提交等场景,数据库均仅存储1条记录。

控制器代码

$referral = new Referral;
$referral->fullname = $request->fullname;
$referral->mobile_phone = $request->phone;
$referral->id_registration = $id_reg;
//$referral etc...
$referral->save();

try {
    Mail::send('mail.user.referral_creation', compact('email', 'fullname', 'user'),
        function ($message) use ($template, $email, $fullname, $main_sender_email, $main_sender_name) {
            $message->from($main_sender_email, $main_sender_name);
            $message->subject($template->greeting);
            $message->to($email, $fullname);
        });
    
    return redirect('/')->with(['success' => 'success']);
} catch (\Swift_TransportException $e) {
    \Log::error($e);
    
    return redirect('/')->with(['error' => 'error']);
} 

Referral模型代码

class Referral extends Model implements Auditable{
use \OwenIt\Auditing\Auditable;
protected $table = 'referrals';
protected $fillable = ['fullname', 'mobile_phone','email',...]
                ];
use SoftDeletes;
protected $guarded = ["id"]; 
protected $dates   = ['deleted_at'];
public $timestamps = true;  }

重复记录时间戳

  • 记录1:2023-08-10 17:22:01 / 2023-08-10 17:22:02(相差1秒)
  • 记录2:2023-08-14 7:30:36 / 2023-08-14 7:30:36(同一秒)
  • 记录3:2023-08-14 17:26:09 / 2023-08-14 17:26:10(相差1秒)

可能的根因分析

  1. 未捕获的后端异常导致前端重试
    当前代码仅捕获了Swift_TransportException,如果Mail::send抛出其他异常(比如视图渲染错误、变量未定义),会直接返回500错误。部分前端或浏览器会自动重试失败的请求,此时两次请求都会执行save逻辑,生成重复记录。

  2. 网络/中间件自动重试
    负载均衡器、CDN或浏览器的网络重试机制,在检测到请求超时或未及时响应时,会自动重发请求。短时间内的重复请求会绕过前端防重逻辑,直接触发后端的插入操作。

  3. 前端特殊场景漏测
    部分浏览器插件、自动填充工具或移动设备的特殊交互,可能导致表单被重复提交,而常规测试场景未覆盖到这类情况。


现有系统避免重复的方案

1. 前端层面

  • 提交按钮点击后立即禁用并添加loading状态,直到请求完成(成功/失败),阻止用户重复点击。
  • 生成唯一表单token:页面加载时生成随机token存入session,提交时携带该token,后端校验后立即失效,避免重复提交。

2. 后端层面

(1)插入前校验重复记录

在执行save前,通过关键字段组合(比如mobile_phone+id_registration,可根据业务添加其他字段)查询是否已存在相同记录,不存在再执行插入:

// 在创建Referral实例前添加校验
$isDuplicate = Referral::where('mobile_phone', $request->phone)
    ->where('id_registration', $id_reg)
    // 可选:添加fullname等其他标识字段
    ->exists();

if (!$isDuplicate) {
    $referral = new Referral;
    $referral->fullname = $request->fullname;
    $referral->mobile_phone = $request->phone;
    $referral->id_registration = $id_reg;
    // 其他字段赋值
    $referral->save();
}

注:高并发场景下可能存在竞态问题,但当前仅出现3条重复,该方案可覆盖绝大多数场景。

(2)扩大异常捕获范围

将整个业务逻辑纳入异常捕获,避免未捕获异常导致的前端重试:

try {
    $isDuplicate = Referral::where('mobile_phone', $request->phone)
        ->where('id_registration', $id_reg)
        ->exists();

    if (!$isDuplicate) {
        $referral = new Referral;
        $referral->fullname = $request->fullname;
        $referral->mobile_phone = $request->phone;
        $referral->id_registration = $id_reg;
        // 其他字段赋值
        $referral->save();
    }

    Mail::send('mail.user.referral_creation', compact('email', 'fullname', 'user'),
        function ($message) use ($template, $email, $fullname, $main_sender_email, $main_sender_name) {
            $message->from($main_sender_email, $main_sender_name);
            $message->subject($template->greeting);
            $message->to($email, $fullname);
        });
    
    return redirect('/')->with(['success' => 'success']);
} catch (\Exception $e) {
    \Log::error($e);
    return redirect('/')->with(['error' => 'error']);
}

(3)事务+悲观锁(可选,解决竞态问题)

如果担心高并发下的竞态问题,可使用事务加悲观锁确保查询和插入的原子性:

DB::transaction(function () use ($request, $id_reg) {
    $isDuplicate = Referral::where('mobile_phone', $request->phone)
        ->where('id_registration', $id_reg)
        ->lockForUpdate()
        ->exists();

    if (!$isDuplicate) {
        $referral = new Referral;
        $referral->fullname = $request->fullname;
        $referral->mobile_phone = $request->phone;
        $referral->id_registration = $id_reg;
        // 其他字段赋值
        $referral->save();
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 03:17:04