Redis分布式鎖的實現

2021-10-10 15:37:24 字數 3696 閱讀 1681

1.方案一

首先傳入需要被鎖的資源id和當前操作的使用者userid,先判斷當前redis中是否有key為id的資料,如果存在,直接返回false代表該資源已經被人使用;如果不存在再插入鍵為id,值為userid,有效時間30s的資料,並且返回true。

/**

* 使用者進入獲取某個key的鎖

* 返回false說明獲取失敗,返回true說明成功

* @return*/(

"getlock1"

)@responsebody

public

boolean

getlock1

(integer id, string userid)

//在redis中插入一條鍵為id,值為userid,有效時間30s的資料

stringredistemplate.

opsforvalue()

.set

(id.

tostring()

, userid,

30, timeunit.seconds)

;return

true

;}

該方法其實存在漏洞,比如當兩個使用者同時進入搶占同一資源,在同一時間查詢到redis中不存在鍵為id的資料,即認為沒有人在使用當前資源,所以都去set資料,而set是可以覆蓋的,導致兩個使用者都看起來上鎖成功了,都會返回true。

模擬一下這種情況:

public

class

test

implements

runnable

@override

public

void

run(

)}

2.方案二

redis本身在set資料時有乙個方法是如果不存在才能插入成功,否則會插入失敗,查閱stringredistemplate的api發現有乙個setifabsent()方法,修改後的**:(

"getlock2"

)@responsebody

public

boolean

getlock2

(integer id, string userid)

//在redis中插入一條鍵為id,值為userid,有效時間30s的資料,使用setifabsent可以在不存在該key的情況下完成插入

boolean res = stringredistemplate.

opsforvalue()

.setifabsent

(id.

tostring()

, userid,

30, timeunit.seconds)

;return res;

}測試結果:

在拿到鎖真正執行邏輯**時,一般會在執行結束後將redis中的資料進行刪除,達到釋放鎖的目的,詳見方案三。

3.方案三

我們在操作之前先呼叫方案二中的getlock方法。成功後,再執行邏輯**,我使用sleep()模擬邏輯**操作所需要的時間,在finally**塊中將鎖釋放。(

"option1"

)@responsebody

public

void

option1

(integer id, string userid)

else

}catch

(interruptedexception e)

finally

else}}

}).start()

;}}使用上面的多執行緒測試工具,將訪問路徑替換為option1,訪問後控制台列印如下:

其實這樣做還有問題,因為真正邏輯進行操作時不一定是10s,有可能是20s,30s,甚至是超過開始設定的redis有效時間(上面設定了30s),所以就存在邏輯**執行的時間超過了有效時間,自動釋放了鎖,其他人在此時就可以拿到鎖,而在邏輯**執行完之後又執行了finally**塊,把別人的鎖釋放掉了,解決方案見方案四。

4.方案四

在加鎖的時候設定隨機值,並存放到redis中,釋放鎖的時候匹配到該隨機值才可以釋放鎖。要加入隨機值的話,redis的value使用hash格式進行儲存較為合理,所以修改後的getlock()方法如下:

public

boolean

getlock3

(integer id, string userid, string radom)

//在redis中插入一條鍵為id,值為userid和隨機值組成的hash,有效時間30s的資料,使用putifabsent可以在不存在該key的情況下完成插入

if(stringredistemplate.

opsforhash()

.putifabsent

(id.

tostring()

,"userid"

, userid)

)return

false

;}

redis視覺化工具檢視效果:

獲得鎖的時候將隨機值儲存成變數,最後在finally中判斷變數和redis中儲存的隨機值是否相同,不相同的話不允許釋放鎖,將執行緒中的sleep時間設定成40s,鎖自動失效時間是30s,就會發生上述的情況,修改後的**如下:(

"option2"

)@responsebody

public

void

option2

(integer id, string userid)

else

}catch

(interruptedexception e)

finally

else

}else}}

}).start()

;}}測試之後控制台列印如下:

鎖已經不屬於當前使用者,但是邏輯**還沒執行完,所以需要加乙個判斷來延續鎖,見方案五。

5.方案五

在邏輯**中加入每隔10s檢測是否超時時間小於等於20s,是的話重新設定超時時間30s,防止鎖失效,在這裡借用上面倒計時**,判斷i是否是10的整數倍來實現10s判斷一次

鎖延續成功,並且最後鎖也是屬於自己的,釋放成功。

至此,redis分布式鎖的實現就較為完善了。

分布式鎖 使用Redis實現分布式鎖

關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...

redis實現分布式鎖

隨便 系統越來越大,各功能模組除了垂直切割以外,同時也得做集群處理,那麼問題來了,在多執行緒情況下對於資源的競爭就需要乙個統一的訪問限制。以選課系統為例子,集群中各節點對課程可選數量同時操作,這裡就需要同步了,否則會導致最後選到的數量比可選的數量大,這裡我們的分布式鎖就派上用場了。利用redis來實...

redis實現分布式鎖

分布式鎖可以基於很多種方式實現,比如zookeeper redis.不管哪種方式,他的 基本原理是不變的 用乙個狀態值表示鎖,對鎖的占用和釋放通過狀態值來標識。1 使用redis的setnx命令實現分布式鎖 1 實現的原理 redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶...