天天看點

redis cluster 分布式鎖_redis實作分布式鎖

redis cluster 分布式鎖_redis實作分布式鎖

什麼是分布式鎖

分布式鎖是控制不同系統通路共享資源的一種鎖機制,保證共享資源的可用、準确。

分布式鎖需要具備什麼條件

1.互斥(必須):同一時刻,分布式部署的應用中,同一個方法/資源隻能被一台機器上的一個線程占用。

2.鎖失效保護(必須):出現用戶端斷電等異常情況,鎖仍然能被其他用戶端擷取,防止死鎖。

3.可重入(可選):同一個線程在沒有釋放鎖之前,如果想再次操作,可以直接獲得鎖。

4.阻塞/非阻塞(可選):若沒有擷取到鎖,傳回擷取失敗

5.高可用、高性能(可選):擷取釋放鎖最好是原子操作,擷取釋放鎖的性能要好

分布式鎖的實作有哪些

1.基于資料庫實作

2.基于緩存(redis,memcached)實作

3.基于zookeeper實作

redis實作方案

本篇文章,我們先來講講基于redis緩存的實作方案

version1

lock:

SETNX key value

unlock:

DEL key [key ...]

指令含義參考:http://doc.redisfans.com/string/setnx.html

這是第一版最簡單的方案,保證在沒有出現任何異常的時候多個用戶端可以使用分布式鎖。

但是問題來了,如下圖中所示,client2在擷取鎖之後突然挂了,這時候鎖k将無法釋放,其他client就永遠拿不到這把鎖了。這就是需要解決的鎖失效保護問題。

redis cluster 分布式鎖_redis實作分布式鎖
version2

我們可以給鎖引入一個過期時間,這樣即使client2挂了,鎖過期之後其他client仍然能用。

EXPIRE key seconds

但此時同樣會存在一些問題:

1)誤删

redis cluster 分布式鎖_redis實作分布式鎖

解決方法是每個client塞給鎖的value設定為唯一的随機字元串,在删除的時候先get一把,如果還是這個字元串的話才去删。

2)過期時間需大于業務執行時間,不然任務還沒搞完就被别人搶了

這個時候需要開啟另外一個線程專門去重新整理鎖的過期時間。

version3

我們需要盡量保證擷取、釋放鎖的操作是原子性的,才能避免極端的異常情況。

原子性地加鎖

SET key uniquevalue NX EX 20

原子性地解鎖

我們可以使用原生的lua腳本

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0           
// java
public void unlock() {
    // 使用lua腳本進行原子删除操作
    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);
}           
version4

對于阻塞/非阻塞的要求,我們可以根據自己的業務特性,如果要阻塞,使用while循環調用;如果要非阻塞,這次調用失敗,就需要增加事後的補償機制。

對于可重入的特性,在一個線程擷取到鎖之後,可以把目前主機資訊和線程資訊儲存起來,下次再擷取之前先檢查自己是不是目前鎖的擁有者。

總結

通過redis實作分布式鎖的必要可選條件之後,方案基本成型了,這個方案可以提供很好的性能。但是對于逾時時間的設定,以及叢集部署redis避免單點問題等還需要進一步優化。

繼續閱讀