Java分布式鎖理論(redis、zookeeper))案例詳解
一、分布式鎖有哪些應(yīng)用場景?
1、定時任務(wù)
2、秒殺搶購,防止庫存超賣的問題
3、雙寫一致性協(xié)議
比如我們?yōu)榱烁呖捎眯源罱朔?wù)集群,分別是8080和8081,我們在項目中設(shè)立定時任務(wù),目的是每天晚上定時拉取用戶數(shù)據(jù),給每個人發(fā)送一些推薦短信。那么這會出現(xiàn)什么問題呢?8080和8081都有定時任務(wù),到半夜2點同時查詢數(shù)據(jù)庫,同時調(diào)用阿里云接口發(fā)短信,那么肯定會重復(fù),使用了分布式鎖,8080搶到鎖執(zhí)行定時任務(wù),那么8081就會阻塞不會執(zhí)行。
那么肯定會有人問,為什么不用synchronized鎖呢?
如果我們是單個項目,用synchronized鎖可以實現(xiàn),但我們用的是集群,synchronized是無法跨jvm的。
二、分布式鎖的實現(xiàn)方案
(1)基于數(shù)據(jù)庫實現(xiàn)——mysql行鎖
(2)基于zookeeper CP模式
(3)基于redis setnx實現(xiàn) AP模式
(4)Redis框架 Redisson、RedisLock
要求:
- 保證一致性:zookeeper 實現(xiàn)分布式鎖
- 保證可用性:redis實現(xiàn)分布式鎖
三、zookeeper實現(xiàn)分布式鎖
zookeeper有個節(jié)點路徑的概念,節(jié)點路徑不能重復(fù),保證了唯一性。
如圖,我有4個springboot項目,首先jvm1先搶到了資源,設(shè)置了zk的節(jié)點路徑/lockPath,這個操作就相當(dāng)于獲取到了鎖,這時其余三個jvm獲取鎖失敗進(jìn)行阻塞狀態(tài)。當(dāng)jvm1執(zhí)行任務(wù)完畢,調(diào)用close()關(guān)閉連接,zk自動刪除節(jié)點路徑釋放鎖,zk通知其余3個jvm節(jié)點,它們3個開始競爭鎖。
一直不釋放鎖怎么辦?
我們上面說的是正常理想情況,那么問題來了,如果jvm1一直不釋放鎖,該怎么辦?
可以采用續(xù)命設(shè)計(設(shè)置超時時間),續(xù)命多次如果業(yè)務(wù)還是沒有執(zhí)行完畢的情況下,則認(rèn)為該鎖超時應(yīng)該主動釋放該鎖,再將所有業(yè)務(wù)代碼回滾,防止其它jvm一直阻塞等待。
如何避免分布式鎖羊群效應(yīng)問題?
如圖可以看出,當(dāng)我們有100個jvm的時候,如果jvm1搶到了鎖,執(zhí)行完業(yè)務(wù)釋放了鎖,zk就要喚醒其余99個jvm,那喚醒這個操作成本是很高的。
如何解決呢?
采用zk的臨時順序節(jié)點。
我們現(xiàn)在有三個jvm,分別創(chuàng)建了三個臨時順序節(jié)點路徑,誰最小就獲取鎖成功,首先jvm1最小獲取鎖成功,jvm2和jvm3就阻塞,jvm2創(chuàng)建的臨時節(jié)點就去訂閱最小的/lockPath1,當(dāng)jvm1執(zhí)行完畢釋放鎖并刪除/lockPath1節(jié)點,那么現(xiàn)在/lockPath2就是最小的節(jié)點,獲取鎖成功。
其實就相當(dāng)于synchronized的公平鎖,jvm1、jvm2、jvm3依次按順序執(zhí)行,這樣我們就不用喚醒所有,jvm1節(jié)點消失,我只需要喚醒jvm2節(jié)點。
四、redis實現(xiàn)分布式鎖
如果不存在值,則返回1,如果存在,則返回0。
那也就是說,我jvm1先setnx返回1搶到了鎖,這時jvm2也setnx發(fā)現(xiàn)返回0,那就無法執(zhí)行業(yè)務(wù)。
當(dāng)我們執(zhí)行業(yè)務(wù)完成后,刪除此key就起到了釋放鎖的作用。
那么問題來了,一個老生常談的話題,如果jvm1一直不釋放鎖怎么辦?
答:先拿setnx來爭搶鎖,搶到之后,再用expire命令給鎖加一個過期時間防止鎖忘記了釋放。
但這樣還有問題,如果在setnx之后執(zhí)行expire之前進(jìn)程意外crash或者要重啟維護(hù)了,那該怎么解決?
答:我們可以使用lua腳本來使setnx+expire成為原子操作。
到此這篇關(guān)于Java分布式鎖理論(redis、zookeeper) 詳解的文章就介紹到這了,更多相關(guān)Java分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談springboot多模塊(modules)開發(fā)
這篇文章主要介紹了淺談springboot多模塊(modules)開發(fā),詳細(xì)的介紹了springboot多模塊的實現(xiàn),有興趣的可以了解一下2017-09-09spring boot 中設(shè)置默認(rèn)網(wǎng)頁的方法
這篇文章主要介紹了spring boot 中設(shè)置默認(rèn)網(wǎng)頁的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04SpringBoot 使用hibernate validator校驗
這篇文章主要介紹了SpringBoot 使用hibernate validator校驗,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11SpringBoot?MongoCustomConversions自定義轉(zhuǎn)換方式
這篇文章主要介紹了SpringBoot?MongoCustomConversions自定義轉(zhuǎn)換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08Java?windows環(huán)境構(gòu)建圖文教程
這篇文章主要為大家介紹了Java?windows環(huán)境構(gòu)建圖文教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2023-12-12