一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

用Redis實現(xiàn)分布式鎖 | Jeff的妙想奇境

 亞典波羅的收藏 2012-07-18

用Redis實現(xiàn)分布式鎖

Redis有一系列的命令,特點是以NX結(jié)尾,NX是Not eXists的縮寫,如SETNX命令就應(yīng)該理解為:SET if Not eXists。這系列的命令非常有用,這里講使用SETNX來實現(xiàn)分布式鎖。

用SETNX實現(xiàn)分布式鎖

利用SETNX非常簡單地實現(xiàn)分布式鎖。例如:某客戶端要獲得一個名字foo的鎖,客戶端使用下面的命令進行獲?。?/p>

SETNX lock.foo <current Unix time + lock timeout + 1>

  •  如返回1,則該客戶端獲得鎖,把lock.foo的鍵值設(shè)置為時間值表示該鍵已被鎖定,該客戶端最后可以通過DEL lock.foo來釋放該鎖。
  •  如返回0,表明該鎖已被其他客戶端取得,這時我們可以先返回或進行重試等對方完成或等待鎖超時。

解決死鎖

上面的鎖定邏輯有一個問題:如果一個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎么解決?我們可以通過鎖的鍵對應(yīng)的時間戳來判斷這種情況是否發(fā)生了,如果當(dāng)前的時間已經(jīng)大于lock.foo的值,說明該鎖已失效,可以被重新使用。

發(fā)生這種情況時,可不能簡單的通過DEL來刪除鎖,然后再SETNX一次,當(dāng)多個客戶端檢測到鎖超時后都會嘗試去釋放它,這里就可能出現(xiàn)一個競態(tài)條件,讓我們模擬一下這個場景:

  1.  C0操作超時了,但它還持有著鎖,C1和C2讀取lock.foo檢查時間戳,先后發(fā)現(xiàn)超時了。
  2.  C1 發(fā)送DEL lock.foo
  3.  C1 發(fā)送SETNX lock.foo 并且成功了。
  4.  C2 發(fā)送DEL lock.foo
  5.  C2 發(fā)送SETNX lock.foo 并且成功了。

這樣一來,C1,C2都拿到了鎖!問題大了!

幸好這種問題是可以避免D,讓我們來看看C3這個客戶端是怎樣做的:

  1. C3發(fā)送SETNX lock.foo 想要獲得鎖,由于C0還持有鎖,所以Redis返回給C3一個0
  2. C3發(fā)送GET lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。
  3. 反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:
    GETSET lock.foo <current Unix time + lock timeout + 1>
  4. 通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如愿以償拿到鎖了。
  5. 如果在C3之前,有個叫C4的客戶端比C3快一步執(zhí)行了上面的操作,那么C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設(shè)置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。

注意:為了讓分布式鎖的算法更穩(wěn)鍵些,持有鎖的客戶端在解鎖之前應(yīng)該再檢查一次自己的鎖是否已經(jīng)超時,再去做DEL操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經(jīng)被別人獲得,這時就不必解鎖了。

示例偽代碼

根據(jù)上面的代碼,我寫了一小段Fake代碼來描述使用分布式鎖的全過程:

  1. # get lock
  2. lock = 0
  3. while lock != 1:
  4.     timestamp = current Unix time + lock timeout + 1
  5.     lock = SETNX lock.foo timestamp
  6.     if lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)):
  7.         break;
  8.     else:
  9.         sleep(10ms)
  10.  
  11. # do your job
  12. do_job()
  13.  
  14. # release
  15. if now() < GET lock.foo:
  16.     DEL lock.foo

是的,要想這段邏輯可以重用,使用python的你馬上就想到了Decorator,而用Java的你是不是也想到了那誰?AOP + annotation?行,怎樣舒服怎樣用吧,別重復(fù)代碼就行。

1
This entry was posted in 技術(shù) and tagged , , , . Bookmark the permalink.

12 Responses to 用Redis實現(xiàn)分布式鎖

  1. Zoom.Quiet says:

    這樣只能作同一主機的不同進程/線程的鎖吧?
    Redis 自個兒不是分布式的哪,這怎么分布式鎖呢?

    • jeff says:

      我所定義的分布式鎖是指支持分布式應(yīng)用(多個進程,多個主機)共享的鎖。而不是說鎖本身是分布式的。
      鎖本身若是分布式,那它的可靠性也就值得商榷了。。

      我使用Redis這個機制實現(xiàn)鎖,就是為了可以讓程序可以實現(xiàn)分布式部署啊。

    • jeff says:

      我理解的分布式鎖中的鎖是指存在應(yīng)用進程外的鎖,傳統(tǒng)的鎖是虛擬機(或進程)級別的鎖,是活在某個進程內(nèi)的,使用虛擬機級別的鎖的程序是沒辦法或很難大規(guī)?;蛯崿F(xiàn)分布式部署的。

      如有理解不當(dāng),懇請大媽指正啊。

      • Sparkle says:

        jeff的思路是對的,分布式鎖是指使用鎖的程序是分布的,一般來說是多個不同的進程,而不是說鎖的實現(xiàn)容器redis本身的實現(xiàn)是不是分布(集群)

  2. Sparkle says:

    為什么不用expire來實現(xiàn)超時?

    • jeff says:

      expire只能讓Key失效,如果有新的進程在Key過期后拿到了新的鎖,原來超時的進程回來如果不經(jīng)判斷會誤認為那是他持有的鎖,會將鎖誤刪了。

      • hoterran says:

        隨機數(shù)和watch可以解決這個問題
        ————-
        random_key = getrandomkey()
        value = redis.setnx(key, random_key)
        if value == 1:
        redis.expire(key, timeout)
        do_job()
        redis.watch(mykey)
        value = redis.get(mykey)
        if random_key == value:
        redis.multi()
        redis.del(mykey)
        redis.exec
        else:
        #不是自己的鎖,所以不能刪除
        else:
        #沒拿到鎖

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日本高清一道一二三区四五区| 欧美韩日在线观看一区| 九九视频通过这里有精品| 午夜视频成人在线观看| 国产伦精品一一区二区三区高清版| 亚洲午夜精品视频观看| 午夜福利国产精品不卡| 国产一区欧美一区日本道| 欧美一区二区三区视频区| 国产一区二区三区口爆在线| 中日韩美女黄色一级片 | 三级理论午夜福利在线看| 日本人妻熟女一区二区三区| 日韩黄色一级片免费收看| 久久99精品日韩人妻| 91欧美激情在线视频| 久久精品国产在热亚洲| 欧美精品一区二区三区白虎| 免费精品国产日韩热久久| 国产一区二区精品高清免费 | 五月天婷亚洲天婷综合网| 国产原创中文av在线播放| a久久天堂国产毛片精品| 亚洲欧洲一区二区综合精品| 日韩精品综合福利在线观看| 日本女优一区二区三区免费| 午夜精品福利视频观看 | 五月天丁香婷婷狠狠爱| 色无极东京热男人的天堂| 亚洲一区二区三区三州| 亚洲中文字幕在线视频频道| 日韩一区二区三区有码| 日韩少妇人妻中文字幕| 91午夜少妇极品福利| 午夜福利国产精品不卡| 亚洲精品黄色片中文字幕| 国产女优视频一区二区| 亚洲中文字幕高清乱码毛片| 日本美国三级黄色aa| 日本免费一本一二区三区| 免费在线播放一区二区|