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