redis系列:分布式鎖

1 介紹

這篇博文講介紹如何一步步構(gòu)建一個(gè)基于Redis的分布式鎖醒叁。會(huì)從最原始的版本開始,然后根據(jù)問題進(jìn)行調(diào)整,最后完成一個(gè)較為合理的分布式鎖。

本篇文章會(huì)將分布式鎖的實(shí)現(xiàn)分為兩部分寨躁,一個(gè)是單機(jī)環(huán)境,另一個(gè)是集群環(huán)境下的Redis鎖實(shí)現(xiàn)牙勘。在介紹分布式鎖的實(shí)現(xiàn)之前职恳,先來了解下分布式鎖的一些信息。

2 分布式鎖

2.1 什么是分布式鎖方面?

分布式鎖是控制分布式系統(tǒng)或不同系統(tǒng)之間共同訪問共享資源的一種鎖實(shí)現(xiàn)话肖,如果不同的系統(tǒng)或同一個(gè)系統(tǒng)的不同主機(jī)之間共享了某個(gè)資源時(shí),往往需要互斥來防止彼此干擾來保證一致性葡幸。

2.2 分布式鎖需要具備哪些條件

互斥性:在任意一個(gè)時(shí)刻最筒,只有一個(gè)客戶端持有鎖。

無死鎖:即便持有鎖的客戶端崩潰或者其他意外事件蔚叨,鎖仍然可以被獲取床蜘。

容錯(cuò):只要大部分Redis節(jié)點(diǎn)都活著,客戶端就可以獲取和釋放鎖

2.4 分布式鎖的實(shí)現(xiàn)有哪些蔑水?

數(shù)據(jù)庫

Memcached(add命令)

Redis(setnx命令)

Zookeeper(臨時(shí)節(jié)點(diǎn))

等等

3 單機(jī)Redis的分布式鎖

3.1 準(zhǔn)備工作

3.1.1 定義常量類

publicclassLockConstants{publicstaticfinalString OK ="OK";/** NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the key if it already exist. **/publicstaticfinalString NOT_EXIST ="NX";publicstaticfinalString EXIST ="XX";/** expx EX|PX, expire time units: EX = seconds; PX = milliseconds **/publicstaticfinalString SECONDS ="EX";publicstaticfinalString MILLISECONDS ="PX";privateLockConstants(){}}復(fù)制代碼

3.1.2 定義鎖的抽象類

抽象類RedisLock實(shí)現(xiàn)java.util.concurrent包下的Lock接口邢锯,然后對(duì)一些方法提供默認(rèn)實(shí)現(xiàn),子類只需實(shí)現(xiàn)lock方法和unlock方法即可搀别。代碼如下

publicabstractclassRedisLockimplementsLock{protectedJedis jedis;protectedString lockKey;publicRedisLock(Jedis jedis,String lockKey){this(jedis, lockKey);? ? }publicvoidsleepBySencond(intsencond){try{? ? ? ? ? ? Thread.sleep(sencond*1000);? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }@OverridepublicvoidlockInterruptibly(){}@OverridepublicConditionnewCondition(){returnnull;? ? }@OverridepublicbooleantryLock(){returnfalse;? ? }@OverridepublicbooleantryLock(longtime, TimeUnit unit){returnfalse;? ? }}復(fù)制代碼

3.2 最基礎(chǔ)的版本1

先來一個(gè)最基礎(chǔ)的版本丹擎,代碼如下

publicclassLockCase1extendsRedisLock{publicLockCase1(Jedis jedis, String name){super(jedis, name);? ? }@Overridepublicvoidlock(){while(true){? ? ? ? ? ? String result = jedis.set(lockKey,"value", NOT_EXIST);if(OK.equals(result)){? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getId()+"加鎖成功!");break;? ? ? ? ? ? }? ? ? ? }? ? }@Overridepublicvoidunlock(){? ? ? ? jedis.del(lockKey);? ? }}復(fù)制代碼

LockCase1類提供了lock和unlock方法。

其中l(wèi)ock方法也就是在reids客戶端執(zhí)行如下命令

SET lockKey value NX復(fù)制代碼

而unlock方法就是調(diào)用DEL命令將鍵刪除歇父。

好了蒂培,方法介紹完了。現(xiàn)在來想想這其中會(huì)有什么問題榜苫?

假設(shè)有兩個(gè)客戶端A和B护戳,A獲取到分布式的鎖。A執(zhí)行了一會(huì)垂睬,突然A所在的服務(wù)器斷電了(或者其他什么的)媳荒,也就是客戶端A掛了抗悍。這時(shí)出現(xiàn)一個(gè)問題,這個(gè)鎖一直存在钳枕,且不會(huì)被釋放缴渊,其他客戶端永遠(yuǎn)獲取不到鎖。如下示意圖

>need-to-insert-img

可以通過設(shè)置過期時(shí)間來解決這個(gè)問題

3.3 版本2-設(shè)置鎖的過期時(shí)間

publicvoidlock(){while(true){? ? ? ? String result = jedis.set(lockKey,"value", NOT_EXIST,SECONDS,30);if(OK.equals(result)){? ? ? ? ? ? System.out.println(Thread.currentThread().getId()+"加鎖成功!");break;? ? ? ? }? ? }}復(fù)制代碼

類似的Redis命令如下

SET lockKey value NX EX 30復(fù)制代碼

注:要保證設(shè)置過期時(shí)間和設(shè)置鎖具有原子性

這時(shí)又出現(xiàn)一個(gè)問題鱼炒,問題出現(xiàn)的步驟如下

客戶端A獲取鎖成功疟暖,過期時(shí)間30秒。

客戶端A在某個(gè)操作上阻塞了50秒田柔。

30秒時(shí)間到了俐巴,鎖自動(dòng)釋放了。

客戶端B獲取到了對(duì)應(yīng)同一個(gè)資源的鎖硬爆。

客戶端A從阻塞中恢復(fù)過來欣舵,釋放掉了客戶端B持有的鎖。

示意圖如下

>need-to-insert-img

這時(shí)會(huì)有兩個(gè)問題

過期時(shí)間如何保證大于業(yè)務(wù)執(zhí)行時(shí)間?

如何保證鎖不會(huì)被誤刪除?

先來解決如何保證鎖不會(huì)被誤刪除這個(gè)問題缀磕。

這個(gè)問題可以通過設(shè)置value為當(dāng)前客戶端生成的一個(gè)隨機(jī)字符串缘圈,且保證在足夠長(zhǎng)的一段時(shí)間內(nèi)在所有客戶端的所有獲取鎖的請(qǐng)求中都是唯一的。

版本2的完整代碼:Github地址

3.4 版本3-設(shè)置鎖的value

抽象類RedisLock增加lockValue字段袜蚕,lockValue字段的默認(rèn)值為UUID隨機(jī)值假設(shè)當(dāng)前線程ID糟把。

public abstract class RedisLock implements Lock {? ? //...? ? protected String lockValue;? ? public RedisLock(Jedis jedis,String lockKey) {? ? ? ? this(jedis, lockKey, UUID.randomUUID().toString()+Thread.currentThread().getId());? ? }? ? public RedisLock(Jedis jedis, String lockKey, String lockValue) {? ? ? ? this.jedis = jedis;? ? ? ? this.lockKey = lockKey;? ? ? ? this.lockValue = lockValue;? ? }? ? //...}復(fù)制代碼

加鎖代碼

public voidlock() {while(true){? ? ? ? String result = jedis.set(lockKey, lockValue, NOT_EXIST,SECONDS,30);if(OK.equals(result)){? ? ? ? ? ? System.out.println(Thread.currentThread().getId()+"加鎖成功!");break;? ? ? ? }? ? }}復(fù)制代碼

解鎖代碼

public voidunlock() {? ? String lockValue = jedis.get(lockKey);if(lockValue.equals(lockValue)){? ? ? ? jedis.del(lockKey);? ? }}復(fù)制代碼

這時(shí)看看加鎖代碼,好像沒有什么問題啊牲剃。

再來看看解鎖的代碼遣疯,這里的解鎖操作包含三步操作:獲取值、判斷和刪除鎖凿傅。這時(shí)你有沒有想到在多線程環(huán)境下的i++操作?

3.4.1 i++問題

i++操作也可分為三個(gè)步驟:讀i的值缠犀,進(jìn)行i+1,設(shè)置i的值聪舒。

如果兩個(gè)線程同時(shí)對(duì)i進(jìn)行i++操作辨液,會(huì)出現(xiàn)如下情況

i設(shè)置值為0

線程A讀到i的值為0

線程B也讀到i的值為0

線程A執(zhí)行了+1操作,將結(jié)果值1寫入到內(nèi)存

線程B執(zhí)行了+1操作箱残,將結(jié)果值1寫入到內(nèi)存

此時(shí)i進(jìn)行了兩次i++操作滔迈,但是結(jié)果卻為1

在多線程環(huán)境下有什么方式可以避免這類情況發(fā)生?

解決方式有很多種,例如用AtomicInteger被辑、CAS燎悍、synchronized等等。

這些解決方式的目的都是要確保i++操作的原子性敷待。那么回過頭來看看解鎖间涵,同理我們也是要確保解鎖的原子性仁热。我們可以利用Redis的lua腳本來實(shí)現(xiàn)解鎖操作的原子性榜揖。

版本3的完整代碼:Github地址

3.5 版本4-具有原子性的釋放鎖

lua腳本內(nèi)容如下

ifredis.call("get",KEYS[1]) == ARGV[1]thenreturnredis.call("del",KEYS[1])elsereturn0end復(fù)制代碼

這段Lua腳本在執(zhí)行的時(shí)候要把的lockValue作為ARGV[1]的值傳進(jìn)去勾哩,把lockKey作為KEYS[1]的值傳進(jìn)去。現(xiàn)在來看看解鎖的java代碼

public voidunlock() {? ? // 使用lua腳本進(jìn)行原子刪除操作? ? String checkAndDelScript ="if redis.call('get', KEYS[1]) == ARGV[1] then "+"return redis.call('del', KEYS[1]) "+"else "+"return 0 "+"end";? ? jedis.eval(checkAndDelScript, 1, lockKey, lockValue);}復(fù)制代碼

好了举哟,解鎖操作也確保了原子性了思劳,那么是不是單機(jī)Redis環(huán)境的分布式鎖到此就完成了?

別忘了版本2-設(shè)置鎖的過期時(shí)間還有一個(gè),過期時(shí)間如何保證大于業(yè)務(wù)執(zhí)行時(shí)間問題沒有解決妨猩。

版本4的完整代碼:Github地址

3.6 版本5-確保過期時(shí)間大于業(yè)務(wù)執(zhí)行時(shí)間

抽象類RedisLock增加一個(gè)boolean類型的屬性isOpenExpirationRenewal潜叛,用來標(biāo)識(shí)是否開啟定時(shí)刷新過期時(shí)間。

在增加一個(gè)scheduleExpirationRenewal方法用于開啟刷新過期時(shí)間的線程壶硅。

public abstract class RedisLock implements Lock {//...? ? protected volatile boolean isOpenExpirationRenewal =true;? ? /**? ? * 開啟定時(shí)刷新? ? */? ? protected voidscheduleExpirationRenewal(){? ? ? ? Thread renewalThread = new Thread(new ExpirationRenewal());? ? ? ? renewalThread.start();? ? }? ? /**? ? * 刷新key的過期時(shí)間? ? */? ? private class ExpirationRenewal implements Runnable{? ? ? ? @Override? ? ? ? public voidrun() {while(isOpenExpirationRenewal){? ? ? ? ? ? ? ? System.out.println("執(zhí)行延遲失效時(shí)間中...");? ? ? ? ? ? ? ? String checkAndExpireScript ="if redis.call('get', KEYS[1]) == ARGV[1] then "+"return redis.call('expire',KEYS[1],ARGV[2]) "+"else "+"return 0 end";? ? ? ? ? ? ? ? jedis.eval(checkAndExpireScript, 1, lockKey, lockValue,"30");? ? ? ? ? ? ? ? //休眠10秒? ? ? ? ? ? ? ? sleepBySencond(10);? ? ? ? ? ? }? ? ? ? }? ? }}復(fù)制代碼

加鎖代碼在獲取鎖成功后將isOpenExpirationRenewal置為true威兜,并且調(diào)用scheduleExpirationRenewal方法,開啟刷新過期時(shí)間的線程庐椒。

publicvoidlock(){while(true) {? ? ? ? String result = jedis.set(lockKey, lockValue, NOT_EXIST, SECONDS,30);if(OK.equals(result)) {? ? ? ? ? ? System.out.println("線程id:"+Thread.currentThread().getId() +"加鎖成功!時(shí)間:"+LocalTime.now());//開啟定時(shí)刷新過期時(shí)間isOpenExpirationRenewal =true;? ? ? ? ? ? scheduleExpirationRenewal();break;? ? ? ? }? ? ? ? System.out.println("線程id:"+Thread.currentThread().getId() +"獲取鎖失敗椒舵,休眠10秒!時(shí)間:"+LocalTime.now());//休眠10秒sleepBySencond(10);? ? }}復(fù)制代碼

解鎖代碼增加一行代碼,將isOpenExpirationRenewal屬性置為false约谈,停止刷新過期時(shí)間的線程輪詢笔宿。

publicvoidunlock(){//...isOpenExpirationRenewal =false;}復(fù)制代碼

版本5的完整代碼:Github地址

3.7 測(cè)試

測(cè)試代碼如下

publicvoidtestLockCase5(){//定義線程池ThreadPoolExecutor pool =newThreadPoolExecutor(0,10,1, TimeUnit.SECONDS,newSynchronousQueue<>());//添加10個(gè)線程獲取鎖for(inti =0; i <10; i++) {? ? ? ? pool.submit(() -> {try{? ? ? ? ? ? ? ? Jedis jedis =newJedis("localhost");? ? ? ? ? ? ? ? LockCase5 lock =newLockCase5(jedis, lockName);? ? ? ? ? ? ? ? lock.lock();//模擬業(yè)務(wù)執(zhí)行15秒lock.sleepBySencond(15);? ? ? ? ? ? ? ? lock.unlock();? ? ? ? ? ? }catch(Exception e){? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? });? ? }//當(dāng)線程池中的線程數(shù)為0時(shí),退出while(pool.getPoolSize() !=0) {}}復(fù)制代碼

測(cè)試結(jié)果

或許到這里基于單機(jī)Redis環(huán)境的分布式就介紹完了棱诱。但是使用java的同學(xué)有沒有發(fā)現(xiàn)一個(gè)鎖的重要特性

那就是鎖的重入泼橘,那么分布式鎖的重入該如何實(shí)現(xiàn)呢?這里就留一個(gè)坑了

4 集群Redis的分布式鎖

在Redis的分布式環(huán)境中迈勋,Redis 的作者提供了RedLock 的算法來實(shí)現(xiàn)一個(gè)分布式鎖炬灭。

4.1 加鎖

RedLock算法加鎖步驟如下

獲取當(dāng)前Unix時(shí)間,以毫秒為單位靡菇。

依次嘗試從N個(gè)實(shí)例担败,使用相同的key和隨機(jī)值獲取鎖。在步驟2镰官,當(dāng)向Redis設(shè)置鎖時(shí),客戶端應(yīng)該設(shè)置一個(gè)網(wǎng)絡(luò)連接和響應(yīng)超時(shí)時(shí)間提前,這個(gè)超時(shí)時(shí)間應(yīng)該小于鎖的失效時(shí)間。例如你的鎖自動(dòng)失效時(shí)間為10秒泳唠,則超時(shí)時(shí)間應(yīng)該在5-50毫秒之間狈网。這樣可以避免服務(wù)器端Redis已經(jīng)掛掉的情況下,客戶端還在死死地等待響應(yīng)結(jié)果笨腥。如果服務(wù)器端沒有在規(guī)定時(shí)間內(nèi)響應(yīng)拓哺,客戶端應(yīng)該盡快嘗試另外一個(gè)Redis實(shí)例。

客戶端使用當(dāng)前時(shí)間減去開始獲取鎖時(shí)間(步驟1記錄的時(shí)間)就得到獲取鎖使用的時(shí)間脖母。當(dāng)且僅當(dāng)從大多數(shù)(這里是3個(gè)節(jié)點(diǎn))的Redis節(jié)點(diǎn)都取到鎖士鸥,并且使用的時(shí)間小于鎖失效時(shí)間時(shí),鎖才算獲取成功谆级。

如果取到了鎖烤礁,key的真正有效時(shí)間等于有效時(shí)間減去獲取鎖所使用的時(shí)間(步驟3計(jì)算的結(jié)果)讼积。

如果因?yàn)槟承┰颍@取鎖失斀抛小(沒有在至少N/2+1個(gè)Redis實(shí)例取到鎖或者取鎖時(shí)間已經(jīng)超過了有效時(shí)間)勤众,客戶端應(yīng)該在所有的Redis實(shí)例上進(jìn)行解鎖(即便某些Redis實(shí)例根本就沒有加鎖成功)。

4.2 解鎖

向所有的Redis實(shí)例發(fā)送釋放鎖命令即可鲤脏,不用關(guān)心之前有沒有從Redis實(shí)例成功獲取到鎖.

關(guān)于RedLock算法们颜,還有一個(gè)小插曲,就是Martin Kleppmann 和?RedLock 作者 antirez的對(duì)RedLock算法的互懟猎醇。 官網(wǎng)原話如下

Martin Kleppmannanalyzed Redlock here. I disagree with the analysis and postedmy reply to his analysis here.

更多關(guān)于RedLock算法這里就不在說明窥突,有興趣的可以到官網(wǎng)閱讀相關(guān)文章。

5 總結(jié)

這篇文章講述了一個(gè)基于Redis的分布式鎖的編寫過程及解決問題的思路硫嘶,但是本篇文章實(shí)現(xiàn)的分布式鎖并不適合用于生產(chǎn)環(huán)境波岛。java環(huán)境有Redisson可用于生產(chǎn)環(huán)境,但是分布式鎖還是Zookeeper會(huì)比較好一些

Java高架構(gòu)師音半、分布式架構(gòu)则拷、高可擴(kuò)展、高性能曹鸠、高并發(fā)煌茬、性能優(yōu)化、Spring boot彻桃、Redis坛善、ActiveMQ、Nginx邻眷、Mycat眠屎、Netty、Jvm大型分布式項(xiàng)目實(shí)戰(zhàn)學(xué)習(xí)架構(gòu)師視頻免費(fèi)學(xué)習(xí)加群:835638062 點(diǎn)擊鏈接加入群聊【Java高級(jí)架構(gòu)】:https://jq.qq.com/?_wv=1027&k=5S3kL3v

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肆饶,一起剝皮案震驚了整個(gè)濱河市改衩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驯镊,老刑警劉巖葫督,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異板惑,居然都是意外死亡橄镜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門冯乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洽胶,“玉大人,你說我怎么就攤上這事裆馒℃⒚ィ” “怎么了丐怯?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)他膳。 經(jīng)常有香客問我响逢,道長(zhǎng)绒窑,這世上最難降的妖魔是什么棕孙? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮些膨,結(jié)果婚禮上蟀俊,老公的妹妹穿的比我還像新娘。我一直安慰自己订雾,他們只是感情好肢预,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著洼哎,像睡著了一般烫映。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上噩峦,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天锭沟,我揣著相機(jī)與錄音,去河邊找鬼识补。 笑死族淮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凭涂。 我是一名探鬼主播祝辣,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼切油!你這毒婦竟也來了蝙斜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤澎胡,失蹤者是張志新(化名)和其女友劉穎乍炉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滤馍,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岛琼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巢株。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片槐瑞。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖阁苞,靈堂內(nèi)的尸體忽然破棺而出困檩,到底是詐尸還是另有隱情祠挫,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布悼沿,位于F島的核電站等舔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏糟趾。R本人自食惡果不足惜慌植,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望义郑。 院中可真熱鬧蝶柿,春花似錦、人聲如沸非驮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劫笙。三九已至芙扎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間填大,已是汗流浹背戒洼。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栋盹,地道東北人施逾。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像例获,于是被迫代替她去往敵國(guó)和親汉额。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 分布式鎖一般有三種實(shí)現(xiàn)方式:1. 數(shù)據(jù)庫樂觀鎖榨汤;2. 基于Redis的分布式鎖蠕搜;3. 基于ZooKeeper...
    程序員技術(shù)圈閱讀 3,804評(píng)論 4 80
  • 前言 分布式鎖一般有三種實(shí)現(xiàn)方式:1. 數(shù)據(jù)庫樂觀鎖;2. 基于Redis的分布式鎖收壕;3. 基于ZooKeeper...
    朦朧蜜桃閱讀 482評(píng)論 1 0
  • 本文首發(fā)于微信公眾號(hào): 小白區(qū)塊鏈成長(zhǎng)記(xiaobaiqukuailian) 這幾天一直有朋友問小白妓灌,因...
    小白區(qū)塊鏈成長(zhǎng)記閱讀 235評(píng)論 0 0
  • 創(chuàng)作《北臺(tái)龍靈》。 2015.10.02 瑪尼堆上彩錦牽蜜宪,石寒冰晶脊髓捐虫埂。 龍王白海福祿綿,北臺(tái)鼎上香燃添圃验。 龍應(yīng)...
    Amine婉婉閱讀 159評(píng)論 0 0
  • 現(xiàn)象1、俗語說“七坐八爬”斧散,家長(zhǎng)們紛紛在孩子七個(gè)月左右教孩子坐供常,然后再教孩子爬行。 現(xiàn)象2鸡捐、看到我家13個(gè)月娃還在...
    sossy在PD路上閱讀 191評(píng)論 0 0