什么是分布式锁及正确使用redis实现分布式锁
?分布式鎖
分布式鎖其實可以理解為:控制分布式系統(tǒng)有序的去對共享資源進行操作,通過互斥來保持一致性。 舉個不太恰當?shù)睦?#xff1a;假設共享的資源就是一個房子,里面有各種書,分布式系統(tǒng)就是要進屋看書的人,分布式鎖就是保證這個房子只有一個門并且一次只有一個人可以進,而且門只有一把鑰匙。然后許多人要去看書,可以,排隊,第一個人拿著鑰匙把門打開進屋看書并且把門鎖上,然后第二個人沒有鑰匙,那就等著,等第一個出來,然后你在拿著鑰匙進去,然后就是以此類推
?實現(xiàn)原理
-
互斥性
-
保證同一時間只有一個客戶端可以拿到鎖,也就是可以對共享資源進行操作
-
-
安全性
-
只有加鎖的服務才能有解鎖權(quán)限,也就是不能讓a加的鎖,bcd都可以解鎖,如果都能解鎖那分布式鎖就沒啥意義了
-
可能出現(xiàn)的情況就是a去查詢發(fā)現(xiàn)持有鎖,就在準備解鎖,這時候忽然a持有的鎖過期了,然后b去獲得鎖,因為a鎖過期,b拿到鎖,這時候a繼續(xù)執(zhí)行第二步進行解鎖如果不加校驗,就將b持有的鎖就給刪除了
-
-
避免死鎖
-
出現(xiàn)死鎖就會導致后續(xù)的任何服務都拿不到鎖,不能再對共享資源進行任何操作了
-
-
保證加鎖與解鎖操作是原子性操作
- 這個其實屬于是實現(xiàn)分布式鎖的問題,假設a用redis實現(xiàn)分布式鎖
-
假設加鎖操作,操作步驟分為兩步:
-
1,設置key set(key,value)2,給key設置過期時間
-
假設現(xiàn)在a剛實現(xiàn)set后,程序崩了就導致了沒給key設置過期時間就導致key一直存在就發(fā)生了死鎖
?如何實現(xiàn)分布式鎖
實現(xiàn)分布式鎖的方式有很多,只要滿足上述條件的都可以實現(xiàn)分布式鎖,比如數(shù)據(jù)庫,redis,zookeeper,在這里就先講一下如何使用redis實現(xiàn)分布式鎖
?使用redis實現(xiàn)分布式鎖
-
使用redis命令 set key value NX EX max-lock-time 實現(xiàn)加鎖
-
使用redis命令 EVAL 實現(xiàn)解鎖
?
加鎖:
Jedis jedis = new Jedis("127.0.0.1", 6379);private static final String SUCCESS = "OK";/*** 加鎖操作* @param key 鎖標識* @param value 客戶端標識* @param timeOut 過期時間*/public Boolean lock(String key,String value,Long timeOut){String var1 = jedis.set(key,value,"NX","EX",timeOut);if(LOCK_SUCCESS.equals(var1)){return true;}return false;}解讀:
-
加鎖操作:jedis.set(key,value,"NX","EX",timeOut)【保證加鎖的原子操作】
-
key就是redis的key值作為鎖的標識,value在這里作為客戶端的標識,只有key-value都比配才有刪除鎖的權(quán)利【保證安全性】
-
通過timeOut設置過期時間保證不會出現(xiàn)死鎖【避免死鎖】
-
NX,EX什么意思?
-
NX:只有這個key不存才的時候才會進行操作,if not exists;
-
EX:設置key的過期時間為秒,具體時間由第5個參數(shù)決定
-
解鎖
Jedis jedis = new Jedis("127.0.0.1", 6379);private static final Long UNLOCK_SUCCESS = 1L;/*** 解鎖操作* @param key 鎖標識* @param value 客戶端標識* @return*/public static Boolean unLock(String key,String value){String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";Object var2 = jedis.eval(luaScript,Collections.singletonList(key), Collections.singletonList(value));if (UNLOCK_SUCCESS == var2) {return true;}return false;}解讀:
-
luaScript 這個字符串是個lua腳本,代表的意思是如果根據(jù)key拿到的value跟傳入的value相同就執(zhí)行del,否則就返回0【保證安全性】
-
jedis.eval(String,list,list);這個命令就是去執(zhí)行l(wèi)ua腳本,KEYS的集合就是第二個參數(shù),ARGV的集合就是第三參數(shù)【保證解鎖的原子操作】
上述就實現(xiàn)了怎么使用redis去正確的實現(xiàn)分布式鎖,但是有個小缺陷就是鎖過期時間要設置為多少合適,這個其實還是需要去根據(jù)業(yè)務場景考量一下的
上面那只是講了加鎖與解鎖的操作,試想一下如果在業(yè)務中去拿鎖如果沒有拿到是應該阻塞著一直等待還是直接返回,這個問題其實可以寫一個重試機制,根據(jù)重試次數(shù)和重試時間做一個循環(huán)去拿鎖,當然這個重試的次數(shù)和時間設多少合適,是需要根據(jù)自身業(yè)務去衡量的
/*** 重試機制* @param key 鎖標識* @param value 客戶端標識* @param timeOut 過期時間* @param retry 重試次數(shù)* @param sleepTime 重試間隔時間* @return*/public Boolean lockRetry(String key,String value,Long timeOut,Integer retry,Long sleepTime){Boolean flag = false;try {for (int i=0;i<retry;i++){flag = lock(key,value,timeOut);if(flag){break;}Thread.sleep(sleepTime);}}catch (Exception e){e.printStackTrace();}return flag;}
到這,用redis實現(xiàn)分布式鎖就寫完了,下次用zookeeper去實現(xiàn)分布式鎖,歡迎留言吐槽 txtx
?
文中set命令詳解:http://redisdoc.com/string/set.html
?
掃碼關注我滴公眾號啊
?
轉(zhuǎn)載于:https://www.cnblogs.com/cmyxn/p/9047848.html
總結(jié)
以上是生活随笔為你收集整理的什么是分布式锁及正确使用redis实现分布式锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。