【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