redis分布式鎖:
1、實現原理
利用redis中的set命令來實現分布式鎖。
從Redis 2.6.12版本開始,set可以使用下列參數:
SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]? ??
EX second :設置鍵的過期時間為second秒。 SET key value EX second效果等同于SETEX key second value 。
PX millisecond :設置鍵的過期時間為millisecond毫秒。 SET key value PX millisecond效果等同于PSETEX key millisecond value 。
NX :只在鍵不存在時,才對鍵進行設置操作。 SET key value NX效果等同于SETNX key value 。
XX :只在鍵已經存在時,才對鍵進行設置操作。
返回值:
SET 在設置操作成功完成時,才返回OK 。
如果設置了NX或者XX ,但因為條件沒達到而造成設置操作未執行,那么命令返回空批量回復(NULL Bulk Reply)。
命令:
> SET key value EX ttl NX
大致思想是:
(a)SET lock currentTime+expireTime EX 600 NX,使用set設置lock值,并設置過期時間為600秒,如果成功,則獲取鎖;
(b)獲取鎖后,如果該節點掉線,則到過期時間ock值自動失效;
(c)釋放鎖時,使用del刪除lock鍵值;
使用redis單機來做分布式鎖服務,可能會出現單點問題,導致服務可用性差,因此在服務穩定性要求高的場合,官方建議使用redis集群(例如5臺,成功請求鎖超過3臺就認為獲取鎖),來實現redis分布式鎖。詳見RedLock。
2、優點
性能高,redis可持久化,也能保證數據不易丟失;
redis集群方式提高穩定性。
3、缺點
使用redis主從切換時可能丟失部分數據。
4、開源實現
python版本的開源實現:python-redis-lock。
redis分布式鎖的具體實現方式:
加鎖和解鎖的方式:
private?static?final?String?LOCK_SUCCESS?=?"OK"; ????private?static?final?String?SET_IF_NOT_EXIST?=?"NX"; ????private?static?final?String?SET_WITH_EXPIRE_TIME?=?"PX"; ????private?static?final?Long?RELEASE_SUCCESS?=?1L;? ????/** ?????*?嘗試獲取分布式鎖 ?????*?@param?lockKey?鎖 ?????*?@param?requestId?請求標識 ?????*?@param?expireTime?超期時間 ?????*?@return?是否獲取成功 ?????*/ ????public?Boolean?tryGetDistributedLock(String?lockKey,?String?requestId,?int?expireTime)?{ ????????Jedis?jedis?=?this.jedisPool.getResource(); ????????String?result?=?jedis.set(lockKey,?requestId,?SET_IF_NOT_EXIST,?SET_WITH_EXPIRE_TIME,?expireTime); ????????if?(LOCK_SUCCESS.equals(result))?{ ????????????return?true; ????????} ????????return?false; ????} ?????/** ?????*?釋放分布式鎖 ?????*?@param?lockKey?鎖 ?????*?@param?requestId?請求標識 ?????*?@return?是否釋放成功 ?????*/ ????public?Boolean?releaseDistributedLock(String?lockKey,?String?requestId)?{ ????????Jedis?jedis?=?this.jedisPool.getResource(); ????????String?script?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then?return?redis.call('del',?KEYS[1])?else?return?0?end"; ????????Object?result?=?jedis.eval(script,?Collections.singletonList(lockKey),?Collections.singletonList(requestId)); ????????if?(RELEASE_SUCCESS.equals(result))?{ ????????????return?true; ????????} ????????return?false; ????}
2、具體的應用
try{ String?requestId?=?UUID.randomUUID().toString(); Boolean?flag?=?tryGetDistributedLock(lock,requestId,1000); int?n?=?0; while(!flag){ ??????//如果沒有獲取鎖,可以嘗試下一個lock,如果都沒有,則嘗試?n?次,退出 ?????... ?????if(n++>5){?throw?new?Exception("嘗試獲取鎖失敗");}?? ?????...?? } if(!flag){ throw?new?Exception("嘗試獲取鎖失敗"); } }catch(){ }finally{ ????releaseDistributedLock(lock,requestId); }
更多Redis相關知識,請訪問Redis使用教程欄目!