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; }