分布式鎖

通過本文檔,你將會了解到

  • 本地鎖不香么恐似?你搞什么分布式鎖杜跷?
  • 分布式鎖的產(chǎn)生背景?
  • 分布式鎖怎么玩蹂喻?以及玩起來的注意事項(xiàng)

1葱椭、 回顧

上次我們講到了,本地緩存的缺陷口四,所以嘍孵运,直接用一個(gè)緩存中間件redis嘍。關(guān)于怎么使用蔓彩,就不扯了治笨,大家都會驳概。接下來主要講的是我們項(xiàng)目中使用到的分布式鎖。

2旷赖、鎖

2.1顺又、本地鎖

本地鎖

說到加鎖 我們有很多方式,比如synchronized,JUC包下的各種各樣的鎖等孵,但是這種鎖只能鎖住當(dāng)前進(jìn)程稚照,我們部署10臺機(jī)器,本地鎖就玩不轉(zhuǎn)了俯萌。所以分布式鎖說那我來吧果录,這也是我們項(xiàng)目中使用到的。我們所有的機(jī)器都先去占坑咐熙,如果占坑成功弱恒,那么你想怎么樣就怎么樣。

所以嘍要使用同一個(gè)鎖棋恼,我們使用redis分布式鎖返弹,當(dāng)然也有ZK鎖。我們講我們項(xiàng)目中使用到的redis分布式鎖爪飘。我們可以同時(shí)去一個(gè)地方“占坑”义起,如果占到,就執(zhí)行邏輯悦施。否則就必須等待并扇,直到釋放鎖。 “占坑”可以去redis抡诞,可以去數(shù)據(jù)庫穷蛹,可以去任何大家都能訪問的地方。 等待可以自旋的方式昼汗。

上次我們講到緩存擊穿的時(shí)候說到過:加鎖肴熏。大量并發(fā)只讓一個(gè)人去查,其他人等待顷窒,查到之后釋放鎖蛙吏,其他人獲取到鎖,先查緩存鞋吉,就會有數(shù)據(jù)鸦做,不用去查數(shù)據(jù)庫

2.2、分布式鎖

2.2.1谓着、介紹

分布式鎖的原理就是占坑唄泼诱,所有的人都想上廁所,那誰占到坑赊锚,那誰上嘍治筒,分布式鎖大概就是這個(gè)原理搞的屉栓。

分布式鎖

廢話不多少,直接上菜唄耸袜,Redis里面那個(gè)是占坑的友多?說到學(xué)習(xí)一門新的知識,那么權(quán)威肯定是官網(wǎng)嘍堤框。
redis官網(wǎng)

http://www.redis.cn/
http://www.redis.cn/commands/set.html

有個(gè)命令比較有特色就是set命令 這個(gè)就是占坑的命令域滥。這個(gè)坑占起來還有講究,hahah
SET 里面放個(gè)key 一個(gè)value 這個(gè)后面可是有可選參數(shù)哦,有的是過期時(shí)間胰锌,有一個(gè)是NX骗绕,所以綜上所述
占坑命令就是
set lock haha NX

SET key value [EX seconds] [PX milliseconds] [NX|XX]

EX seconds – Set the specified expire time, in seconds.
PX milliseconds – Set the specified expire time, in milliseconds.
NX – Only set the key if it does not already exist.
XX – Only set the key if it already exist.
EX seconds – 設(shè)置鍵key的過期時(shí)間藐窄,單位時(shí)秒
PX milliseconds – 設(shè)置鍵key的過期時(shí)間资昧,單位時(shí)毫秒
NX – 只有鍵key不存在的時(shí)候才會設(shè)置key的值
XX – 只有鍵key存在的時(shí)候才會設(shè)置key的值


注意: 由于SET命令加上選項(xiàng)已經(jīng)可以完全取代SETNX, SETEX, PSETEX的功能,所以在將來的版本中荆忍,redis可能會不推薦使用并且最終拋棄這幾個(gè)命令格带。

2.2.2、分布式鎖的演進(jìn)過程

巧了刹枉,我懂了叽唱,直接用set lock haha NX 分布式鎖搞的,分享結(jié)束微宝。你覺得會是這么簡單么棺亭?不要太天真,到時(shí)候發(fā)現(xiàn)小丑竟是我自己hahaha蟋软。

2.2.2.1镶摘、青銅玩家

來上菜。我們先看青銅玩家怎么玩岳守?

Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "11111");
if (lock) {
 
    try {
        //加鎖成功...執(zhí)行業(yè)務(wù)
        XXXXXXX
        //刪除
        stringRedisTemplate.delete(XXXXXX);
    }else{
        //重試 自旋
        1.自己調(diào)取自己
        2.可以休眠1S
    }

你看看青銅玩家凄敢,一看就是新手。一眼就看出來的錯誤就是異常了沒人解鎖了湿痢。
解決:

  • 設(shè)置鎖的自動過期涝缝,即使沒有刪除,會自動刪除譬重。
  • try 包裹 finally解鎖

2.2.2.2拒逮、白銀玩家

 Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,300,TimeUnit.SECONDS);

if (lock) {
 
    try {
        //加鎖成功...執(zhí)行業(yè)務(wù)
    }else{
        //重試 自旋
        1.自己調(diào)取自己
        2.可以休眠1S
    } finally{
     //刪除
        stringRedisTemplate.delete(XXXXXX);
}

你看看白銀玩家,有點(diǎn)那么個(gè)意思了是吧臀规?那白銀玩家有沒有問題呢滩援?階段才是白銀了,那肯定是有問題的以现,關(guān)鍵是問題在哪里狠怨?

1约啊、刪除鎖直接刪除?佣赖?恰矩?
如果由于業(yè)務(wù)時(shí)間很長,鎖自己過期了憎蛤,我們直接刪除外傅,有可能把別人正在持有的鎖刪除了。

2俩檬、解決:

  • 占鎖的時(shí)候萎胰,值指定為uuid,每個(gè)人匹配是自己的鎖才刪除棚辽。技竟。

2.2.2.2、黃金玩家

黃金玩家

關(guān)鍵是問題在哪里屈藐?

問題:
1榔组、如果正好判斷是當(dāng)前值,正要刪除鎖的時(shí)候联逻,鎖已經(jīng)過期搓扯, 別人已經(jīng)設(shè)置到了新的值。那么我們刪除的是別人的鎖包归。

2锨推、解決:

  • 刪除鎖必須保證原子性。使用redis+Lua腳本完成

2.2.2.2公壤、鉆石玩家

//1换可、占分布式鎖。去redis占坑      設(shè)置過期時(shí)間必須和加鎖是同步的境钟,保證原子性(避免死鎖)
String uuid = UUID.randomUUID().toString();
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,300,TimeUnit.SECONDS);
if (lock) {
    System.out.println("獲取分布式鎖成功...");
    Map<String, List<Catelog2Vo>> dataFromDb = null;
    try {
        //加鎖成功...執(zhí)行業(yè)務(wù)
        dataFromDb = getDataFromDb();
    } finally {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        //刪除鎖
        stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);

    }

當(dāng)然線上你可以這么寫锦担,但是畢竟是鉆石玩家,我們還有更好的玩法慨削。

2.3洞渔、Redisson 作為分布式鎖

這個(gè)也是我們項(xiàng)目中使用到的。廢話不多說缚态,第一步打開官網(wǎng)磁椒,開啟尋寶之路。

https://www.redis.io/topics/distlock

分布式鎖的介紹頁面


分布式鎖

2.3.1 Redisson 是什么

一種 可重入玫芦、持續(xù)阻塞浆熔、獨(dú)占式的 分布式鎖協(xié)調(diào)框架。
Redisson的宗旨是促進(jìn)使用者對Redis的關(guān)注分離(Separation of Concern)桥帆,從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上医增。

特點(diǎn):

  • 可重入
    拿到鎖的線程后續(xù)拿鎖可跳過獲取鎖的步驟慎皱,只進(jìn)行value+1的步驟。
  • 持續(xù)阻塞
    獲取不到鎖的線程叶骨,會在一定時(shí)間內(nèi)等待鎖茫多。
  • 獨(dú)占式
    同一環(huán)境下理論上只能有一個(gè)線程可以獲取到鎖

比較爽的一點(diǎn)是什么?
基于Redis的Redisson分布式可重入鎖RLock Java對象實(shí)現(xiàn)了java.util.concurrent.locks.Lock接口忽刽。

這句話什么意思天揖?如果你已經(jīng)了解了JUC包下的各種鎖,什么ReentrantLock跪帝,信號量什么的今膊。恭喜你無縫切換。就像你換手機(jī)一樣伞剑,iPhone12換iPhone13 像流水切換一樣自然斑唬。以前怎么用,現(xiàn)在還怎么用纸泄。

關(guān)于并發(fā)編程JUC的知識不在本次分享范圍內(nèi)赖钞。

      //1、獲取一把鎖聘裁,只要鎖的名字一樣,就是同一把鎖
        RLock myLock = redisson.getLock("my-lock");

        //2弓千、加鎖
        myLock.lock();      //阻塞式等待衡便。默認(rèn)加的鎖都是30s
        //1)、鎖的自動續(xù)期洋访,如果業(yè)務(wù)超長镣陕,運(yùn)行期間自動鎖上新的30s。不用擔(dān)心業(yè)務(wù)時(shí)間長姻政,鎖自動過期被刪掉
        //2)呆抑、加鎖的業(yè)務(wù)只要運(yùn)行完成,就不會給當(dāng)前鎖續(xù)期汁展,即使不手動解鎖鹊碍,鎖默認(rèn)會在30s內(nèi)自動過期,不會產(chǎn)生死鎖問題
        // myLock.lock(10,TimeUnit.SECONDS);   //10秒鐘自動解鎖,自動解鎖時(shí)間一定要大于業(yè)務(wù)執(zhí)行時(shí)間
        //問題:在鎖時(shí)間到了以后食绿,不會自動續(xù)期
        //1侈咕、如果我們傳遞了鎖的超時(shí)時(shí)間,就發(fā)送給redis執(zhí)行腳本器紧,進(jìn)行占鎖耀销,默認(rèn)超時(shí)就是 我們制定的時(shí)間
        //2、如果我們指定鎖的超時(shí)時(shí)間铲汪,就使用 lockWatchdogTimeout = 30 * 1000 【看門狗默認(rèn)時(shí)間】
        //只要占鎖成功熊尉,就會啟動一個(gè)定時(shí)任務(wù)【重新給鎖設(shè)置過期時(shí)間罐柳,新的過期時(shí)間就是看門狗的默認(rèn)時(shí)間】,每隔10秒都會自動的再次續(xù)期,續(xù)成30秒
        // internalLockLeaseTime 【看門狗時(shí)間】 / 3狰住, 10s
        try {
            System.out.println("加鎖成功硝清,執(zhí)行業(yè)務(wù)..." + Thread.currentThread().getId());
            try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            //3、解鎖  假設(shè)解鎖代碼沒有運(yùn)行转晰,Redisson會不會出現(xiàn)死鎖
            System.out.println("釋放鎖..." + Thread.currentThread().getId());
            myLock.unlock();
        }


關(guān)于看門狗芦拿,就是業(yè)務(wù)執(zhí)行需要10秒,還沒執(zhí)行完就解鎖了查邢?看門狗自動幫你續(xù)命時(shí)間(這個(gè)是一個(gè)定時(shí)任務(wù)蔗崎,沒10秒執(zhí)行一次看看要不要續(xù)命)當(dāng)前線程活著就續(xù)命,線程(而key的組成應(yīng)該是:{uuid}:{threadid})只有沒有指定過去時(shí)間才會啟動扰藕。

最佳實(shí)踐:我們指定過期時(shí)間缓苛,性能會有一點(diǎn)增加,指定3秒過期時(shí)間邓深,3秒都沒執(zhí)行完未桥,那早就超時(shí)了什么的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芥备,一起剝皮案震驚了整個(gè)濱河市冬耿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萌壳,老刑警劉巖亦镶,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異袱瓮,居然都是意外死亡缤骨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門尺借,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绊起,“玉大人,你說我怎么就攤上這事燎斩∈幔” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵瘫里,是天一觀的道長实蔽。 經(jīng)常有香客問我,道長谨读,這世上最難降的妖魔是什么局装? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上铐尚,老公的妹妹穿的比我還像新娘拨脉。我一直安慰自己,他們只是感情好宣增,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布玫膀。 她就那樣靜靜地躺著,像睡著了一般爹脾。 火紅的嫁衣襯著肌膚如雪帖旨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天灵妨,我揣著相機(jī)與錄音解阅,去河邊找鬼。 笑死泌霍,一個(gè)胖子當(dāng)著我的面吹牛货抄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朱转,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蟹地,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了藤为?” 一聲冷哼從身側(cè)響起怪与,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凉蜂,沒想到半個(gè)月后琼梆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窿吩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了错览。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纫雁。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖倾哺,靈堂內(nèi)的尸體忽然破棺而出轧邪,到底是詐尸還是另有隱情,我是刑警寧澤羞海,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布忌愚,位于F島的核電站,受9級特大地震影響却邓,放射性物質(zhì)發(fā)生泄漏硕糊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望简十。 院中可真熱鬧檬某,春花似錦、人聲如沸螟蝙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胰默。三九已至场斑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牵署,已是汗流浹背漏隐。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碟刺,地道東北人锁保。 一個(gè)月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像半沽,于是被迫代替她去往敵國和親爽柒。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

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

  • @[toc] 一者填、緩存 1浩村、緩存使用 為了系統(tǒng)性能的提升,我們一般都會將部分?jǐn)?shù)據(jù)放入緩存中占哟,加速訪問心墅。而db承擔(dān)數(shù)...
    runewbie閱讀 823評論 0 1
  • 1 Redis分布式鎖的特性 在實(shí)現(xiàn)分布式鎖時(shí),需要保證鎖實(shí)現(xiàn)的安全性和可靠性榨乎≡踉铮基于這點(diǎn)特點(diǎn),實(shí)現(xiàn)分布式鎖需要具備...
    愛健身的兔子閱讀 920評論 0 0
  • 1. 分布式應(yīng)用并發(fā)問題 分布式應(yīng)用進(jìn)行邏輯處理時(shí)經(jīng)常會遇到并發(fā)問題蜜暑,比如下面這個(gè)例子铐姚。 在Redis里對"acc...
    逍遙白亦閱讀 369評論 0 5
  • 1 本地鎖 常用的即 synchronize 或 Lock 等 JDK 自帶的鎖,只能鎖住當(dāng)前進(jìn)程肛捍,僅適用于單體架...
    半壺雪閱讀 292評論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月隐绵,有人笑有人哭,有人歡樂有人憂愁拙毫,有人驚喜有人失落依许,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,542評論 28 53