天天看點

分布式鎖之Redis分布式鎖

首先需要了解下基本的原理: 多個redis用戶端執行setnx指令,設定一個相同的key,誰能夠建立key成功,誰就能夠擷取鎖,當key建立成功後,會傳回true,說明加鎖成功,其他用戶端請求就無法擷取鎖,就會直接傳回false,搶鎖失敗,這樣確定隻有一個用戶端請求執行。

具體指令如下:

将 key 的值設為 value ,當且僅當 key 不存在。

若給定的 key 已經存在,則 SETNX 不做任何動作。

SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。

傳回值:

設定成功,傳回 1 。

設定失敗,傳回 0 。

題外話:對于指令

為指定的 key 設定值及其過期時間。如果 key 已經存在, SETEX 指令将會替換舊的值。

既然設定了value值,那麼我們肯定會想到過期時間,那麼就需要再使用setnx指令後繼續使用expire指令。但是這兩個操作必定不是原子性的,如果執行expire失敗或者執行完setnx指令後redis伺服器當機,那麼這就會導緻這個key一直存在于redis,産生死鎖。

在Redis2.8 之後,官方執行setnx 和 expire指令一起使用了。如下:

lock_key:即鎖名稱,這個名稱應是公開的,在分布式環境中,對于某一确定的公共資源,所有争用方(用戶端)都應該知道對應鎖的名字。對于 Redis 而言,lock_name 就是 key-value 中的 key,具有唯一性。

lock_value:是由用戶端生成的一個随機字元串,它要保證在足夠長的一段時間内在所有用戶端的所有擷取鎖的請求中都是唯一的,用于唯一辨別鎖的持有者。

NX 表示隻有當 lock_key(key) 不存在的時候才能 SET 成功,進而保證隻有一個用戶端能獲得鎖,而其它用戶端在鎖被釋放之前都無法獲得鎖。

PX 30000 表示這個鎖節點有一個 30 秒的自動過期時間(目的是為了防止持有鎖的用戶端故障後,無法主動釋放鎖而導緻死鎖,是以要求鎖的持有者必須在過期時間之内執行完相關操作并釋放鎖)。

不過在使用SET方法的時候也要注意:

例如我們有兩個線程A、B,此時線程A搶到了鎖,且設定自動過期時間為10s鐘,因為系統其他原因導緻系統A發生阻塞。而此刻10s鐘後鎖自動過期,線程C擷取到了同一個資源的鎖,線程A從阻塞中恢複,認為自己仍然持有鎖,繼續操作同一資源。這樣就使得加鎖的互斥性失效了。

解決方案:

我們在上面set lock_key lock_value 時講過,lock_value是一個随機生成的字元串,在每次擷取鎖的時候都會重新生成。那麼我們在執行真正的業務邏輯(類似于和db進行互動的操作,同一時刻隻能一個線程操作的情況)時,判斷目前生成的随機字元串和lock_value是否一緻,如果不一緻則說明redis中的lock_value被修改過,也就說明此刻鎖已經被其他線程所占有。

使用類似于redisson “看門狗” 的機制,當發現還未執行完業務邏輯但是鎖又過期的情況,那麼延長鎖的存活時間。

1. RedisTemplate:

2. Redisson:

3. Jedis:

4. Lettuce:

繼續閱讀