【Redis】使用redis+lua实现redis的分布式锁,控制电商中库存超卖问题(不是秒杀,是高并发常态化处理,秒杀还是队列)


1、 相关参考文章:

首推看看这篇 https://blog.csdn.net/She_lock/article/details/88894096

2、至于为什么不用setnx或者setnx+时间戳模式。因为他们都有问题,前者产生死锁;后者产生误删锁现象。

3、核心:使用redis2.6+以上版本,使用

上锁:redis命令【set key value NX PX 20000】

解锁:lua脚本

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

完美解决 高并发下 分布式事务的资源抢占、死锁、误删锁问题。

 

4、废话不多说,献上laravel5的代码,供参考: -- (tips:安装了redis2.6+后,可直接在redis内部运行lua脚本)

    /**
     * 获取商品对应库存 -- redis单例+lua的分布式锁控制流程
     * redis官方推荐写法优点:px避免死锁、lua避免误删锁
     * @param $id  //商品ID_门店Id
     * @param $nodeId
     * @return object
     */
    public static function getHashStockNew( $skuId, $nodeId )
    {
        $stockNum = WeChatDao::systemParamRedis('get', $skuId.'_'.$nodeId, null, ProductDao::REDIS_KEY_PRODUCT_STOCK_ERP);
        if (!isset($stockNum)) {
            $lockKey = config( 'cache.redis_cache_prefix' ) . ':stock_erp_stock_cache';
            // 删锁Lua控制get/del原子性
            $delLockScript = <<<LUA
if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end
LUA;
            for ( $i = 1; $i <= 3; $i++) {
                //set nx px加锁 -- lua实现
                $lockValue = (string)AgentDao::getMillisecond().'_'. mt_rand(11111,99999);
                $addLockRes = Redis::set($lockKey, $lockValue, 'nx', 'px', 30000);
                if($addLockRes == 'OK'){
                    // 判断是否sku商品
                    //echo $nodeId,$skuId;
                    $objM = ProductStockErp::where('store_id',$nodeId)->where('sku_id', $skuId)->first();
                    if (empty($objM)) {
                        //解锁
                        $delLockRes = Redis::eval($delLockScript, 1, $lockKey, $lockValue);// 返回被删除 key 的数量int
                        if(0 === $delLockRes) S::error(40002, '系统超时,请稍后继续~');
                        //WeChatDao::baseRedis('del', ':stock_erp_stock_cache');
                        S::error('70006');
                    }
                    //缓存库存信息
                    WeChatDao::systemParamRedis('set', $skuId.'_'.$nodeId, $objM->remain_num, ProductDao::REDIS_KEY_PRODUCT_STOCK_ERP);
                    //解锁
                    $delLockRes = Redis::eval($delLockScript, 1, $lockKey, $lockValue);
                    if(0 === $delLockRes) {
                        // 回滚操作
                        WeChatDao::systemParamRedis('del', $skuId.'_'.$nodeId, null, ProductDao::REDIS_KEY_PRODUCT_STOCK_ERP);
                        S::error(40002, '系统超时,清稍后继续!!');
                    }
                    
                    $stockNum = $objM->remain_num;
                    break;
                } else {
                    sleep(1);
                    continue;
                }
            }
        }
        return $stockNum;
    }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM