百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

在B端业务中由于重复点或并发造成的重复数据处理思路

wptr33 2025-04-09 21:25 42 浏览

需求背景:

入库单号:L-ASN2025022xxxxx

入库批次号:100025022800151

数量:500

理货后,生成了一个500的已理货数据,上架后,生成了两个500已上架数据,重复了



后台查看数据创建时间也一样,仓库操作人员由于网络或者其他问题出现重复点击或者在多人操作同一单据情况下出现的并发问题

这种情况下仅仅针对接口加分布式锁解决不了实际问题,需要针对请求url,参数进行加锁才可以完全解决

以php项目为例 php-laravel框架

1,通过url+请求参数进行加锁处理 可以组织90%以上的重复点击带来的重复数据的问题

PreventDupSubmit.php

all();
        $requestParams["adminId"] = getAdminUserId();
        $requestParams["uri"] = $request->getUri();
        if(isset($requestParams["file"])){
            unset($requestParams["file"]);
        }
        \Log::info(sprintf("并发请求url:%s,请求参数:%s",$request->getUri(),json_encode($requestParams)));
        ksort($requestParams);
        $key = md5(serialize($requestParams));

        $getLock = WmsLockService::getLock($key,8);
//        $request->offsetSet("request_md5_key",$key);


        if(!$getLock){
            return response()->json(json_decode(Response::setError("请勿重复请求,请5秒后再发起请求"), true));
        }

//        $result = Redis::Connection("wms")->set('wms_' . $key, 1,"EX",5,"NX");
//        if(!$result){
//            throw new InvalidRequestException("请勿重复请求,请5秒后再发起请求");
//        }
        $response = $next($request);
        WmsLockService::delLock($key);
//        Redis::Connection("wms")->del('wms_' . $key);
        return $response;
    }
}
WmsLockService.php
<?php

namespace App\Http\Services;

use Illuminate\Support\Facades\Redis;

class WmsLockService
{
    const LOCK_KEY = "wms_lock_";

    public static function getLock($Key="",$time=15){
        $requestId = uniqid(); // 生成一个唯一的请求ID
        $expireTime = 1000 * intval($time); // 锁的过期时间,单位毫秒

        // Lua脚本
        $luaScript = <<eval($luaScript, 1, $lockKey, $requestId, $expireTime);
        return $result;
    }



    public static function delLock($lockKey=""){
        Redis::Connection("wms")->del(self::LOCK_KEY.$lockKey);
    }

}

路由

 Route::match(['get', 'post'], '/stockIn/stockShelf/oneKeyPutawayAction', 'StockShelfController@oneKeyPutawayAction')->middleware('prevent_dup_submit')

中间件


2,假如业务中出现非重复点击,或者由于事务中处理比较慢(请求上架或者理货 请求参数类似于[1,3,5] [1,3,8,9]),两次请求中都有请求参数id:1,3 会造成1和3的并发问题。

处理方法就是针对具体请求的函数对单个参数进行加锁 当上架id:[1,3,5]请求的时候可以让1,3,5分别进行加锁,下一个1,3,8,9来请求的时候就会因为拿不到锁而报错


RedisLock
systemName = $systemName;
        $this->functionName = $functionName;
    }

    public static function initInstance($systemName, $functionName)
    {
        return new self($systemName, $functionName);
    }

    public function lock($key, $ttl = null)
    {
        $redis = Redis::connection();
        $randNum = mt_rand(10000000, 99999999);
        $ttl = $ttl ?? self::TTL;
        $redisKey = self::KEY_PREFIX . ":{$this->systemName}:{$this->functionName}:{$key}";
        $result = $redis->setNx($redisKey, $randNum);
        if ($result) {
            $redis->expire($redisKey, $ttl);
            $this->randNumMap[$key] = $randNum;
            return $randNum;
        } else {
            return null;
        }
    }

    public function unlock($key, $randNum)
    {
        $redis = Redis::connection();
        $redisKey = self::KEY_PREFIX . ":{$this->systemName}:{$this->functionName}:{$key}";
        $lua = <<eval($lua, 1, $redisKey, $randNum);
        return $result;
    }

    public function lockFunction($key, callable $callback,callable $failCallback = null)
    {
        $random = $this->lock($key);
        if ($random !== null) {
            $result = call_user_func($callback);
            $delResult = $this->unlock($key, $random);
            if ($delResult==0){
                if ($failCallback){
                    call_user_func($failCallback);
                }
            }
            return $result;
        } else {
            return false;
        }
    }

    public function lockBatchKey($keyArr, $ttl = null)
    {
        $lockResult = [];
        foreach ($keyArr as $key) {
            $result = $this->lock($key, $ttl);
            if ($result) {
                $lockResult[$key] = $result;
            } else {
                $lockResult[$key] = 0;
            }
        }
        return $lockResult;
    }

    public function unlockBatchKey($resultArr)
    {
        $unlockResult = [];
        foreach ($resultArr as $key => $random) {
            $random = $this->randNumMap[$key] ?? $random;
            $result = $this->unlock($key, $random);
            if ($result) {
                $unlockResult[$key] = true;
            } else {
                $unlockResult[$key] = false;
            }
        }
        return $unlockResult;
    }
}

在开发过程中刚开始使用的是上述第一种方法,但是偶尔3-6个月会出现一次重复数据,目前没有找到具体原因,所以后来又采用第二种更加细粒度的加锁方式,对请求的具体参数进行加锁,希望能帮到大家解决开发过程中遇到类似的问题提供解决思路

相关推荐

oracle数据导入导出_oracle数据导入导出工具

关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...

继续学习Python中的while true/break语句

上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个...

python continue和break的区别_python中break语句和continue语句的区别

python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...

简单学Python——关键字6——break和continue

Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...

2-1,0基础学Python之 break退出循环、 continue继续循环 多重循

用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...

Python 中 break 和 continue 傻傻分不清

大家好啊,我是大田。...

python中的流程控制语句:continue、break 和 return使用方法

Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...

L017:continue和break - 教程文案

continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...

作为前端开发者,你都经历过怎样的面试?

已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...

面试被问 const 是否不可变?这样回答才显功底

作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。...

前端面试总结_前端面试题整理

记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...

由浅入深,66条JavaScript面试知识点(七)

作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录...

2024前端面试真题之—VUE篇_前端面试题vue2020及答案

添加图片注释,不超过140字(可选)...

今年最常见的前端面试题,你会做几道?

在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...