先查询mysql数据库是否存在数据,不存在插入,如何避免重复插入

场景:小程序用户扫码成为会员
扫码缓存=》未登录=>跳转登录=>请求接口成为会员(先查询mysql数据库是否存在数据,不存在插入。)
由于前端用钩子函数的时候重复调用,重复请求了2次,即使设置了唯一索引,经常会出现重复插入报错
除了前端处理,后端应该如何避免这个问题发生?

补充:成为会员的时候还会做其他大量的处理

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

使用redis锁

        //防止重复提交 加redis锁
        $key = '_punch_'.$user['id'].'_'.$data['landmark_pk'];//自定义的key
        $rs = RedisLocal::get_redis();//换成自己的redis对象
        $ttl = 86400;
        $lock = $rs->set($key, $key, ['nx', 'ex' => $ttl]);
        if (!$lock) {
            throw new BaseException(['code' => -1, 'msg' => '请不要频繁提交哦~']);
        }
        //$res = $this->checkPrize($params);  //自己的业务
        if ($rs->get($key) == $key) {
            //如果系统是负载均衡的 有可能同一时间的多次提交打在了不同的服务器上 这里按实际需要要sleep几秒再删除锁
            $rs->del($key);
        }
5天前 评论
╰ゝSakura 4天前
my38778570 (楼主) 4天前
xtheme 4天前
Aroad 4天前
讨论数量: 20

接口做幂等处理,此处后端可用限流器或加锁解决

2周前 评论

利用mysql的唯一索引

2周前 评论
my38778570 (楼主) 2周前

数据库保证不出现重复记录就是唯一索引,程序中可以捕获异常友好的返回,场景并不存在并发,这种异常实际情况一般不会发生,把前端改过来再说

2周前 评论
my38778570 (楼主) 2周前

有自带功能

User::updateOrCreate(); // 会直接新增
User::firstOrNew(); // 需手动调用 save()

结合确定唯一字段创建主键或唯一索引

2周前 评论
my38778570 (楼主) 2周前
mowangjuanzi

可以尝试在创建这一块进行加锁。创建完成后解锁,然后在创建这个位置只允许一个接口处理,重复请求的时候进行等待。

我这里说的只允许一个接口请求,针对的是同一个 openid 多次请求,只先处理一个,剩下的先挂着,等第一个处理完了别的请求再去申请锁

2周前 评论
gema

尝试使用同步队列吧 在队列中同时只会有一个添加新会员的逻辑在跑 每次跑的时候先判断是否存在再新建 应该就不会重复了, 然后前端做一下防抖 比如点击注册 两秒内不能再点之类的

2周前 评论
DB::transaction(function (){
   $user = User::lockForUpdate()->first(); 
   // 后续sql操作
});
// 您可以使用 lockForUpdate 方法。「update」锁可防止所选记录被修改或被另一个共享锁选中:

我感觉直接悲观锁就行了

2周前 评论
wade 3天前

直接依靠Mysql的唯一,出错就抓异常,标识重复了

            try {
                UserModel::insert(...);

            } catch (\PDOException $e) {
                throw new \Exception("用户已存在");
            }

查询再判断本身就无法阻止并发下重复入库,只有依靠锁或者unique,如果业务允许加unique,本写法在并发场景下可以节省一次mysql查询,减少了锁操作,并发场景不二之选

1周前 评论

主键不连续不是正常吗 不用纠结这个吧 我们现在用的分布式主键

1周前 评论
SevenMonths 4天前
人艰不拆 (作者) 4天前

使用redis锁

        //防止重复提交 加redis锁
        $key = '_punch_'.$user['id'].'_'.$data['landmark_pk'];//自定义的key
        $rs = RedisLocal::get_redis();//换成自己的redis对象
        $ttl = 86400;
        $lock = $rs->set($key, $key, ['nx', 'ex' => $ttl]);
        if (!$lock) {
            throw new BaseException(['code' => -1, 'msg' => '请不要频繁提交哦~']);
        }
        //$res = $this->checkPrize($params);  //自己的业务
        if ($rs->get($key) == $key) {
            //如果系统是负载均衡的 有可能同一时间的多次提交打在了不同的服务器上 这里按实际需要要sleep几秒再删除锁
            $rs->del($key);
        }
5天前 评论
╰ゝSakura 4天前
my38778570 (楼主) 4天前
xtheme 4天前
Aroad 4天前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!