當前位置:
首頁 > 最新 > 使用redis實現分散式鎖實踐

使用redis實現分散式鎖實踐

分散式鎖在多實例部署,分散式系統中經常會使用到,這是因為基於jvm的鎖無法滿足多實例中鎖的需求,本篇將講下redis如何通過Lua腳本實現分散式鎖,不同於網上的redission,完全是手動實現的

我們先來看一個無鎖的情況下會導致什麼問題:

這是一個普通的更新用戶年齡的功能,各層代碼如下,訪問controller層,一個更新,一個查詢

這是service層,我們使用contdownlatch發令槍來模擬線程同時並發的情況,發令槍設為32,即32個線程同時去請求修改年齡,

這裡使用線程池來提交多線程任務,看代碼知道,這裡我們已經有了判斷年齡的操作,當查詢用戶查詢大於0時,才去調更新用戶年齡-1的方法,等下看看有沒有用

這裡是sql,可以看到兩個sql,一個查詢用戶年齡,一個會執行用戶年齡每次減1 ,

這裡是用戶數據,我們可以看到,用戶UID為UR12324的用戶,他的年齡是30,接著我們來調32個線程來操作減他年齡

我們請求下這個方法

然後看看結果:

可以看到庫中年齡已被減為-2,在未加鎖的情況下,查詢較驗並沒有什麼作用,此時如果加個synchronized或lock鎖肯定能避免這種情況,但我們本文討論的是多實例或分散式環境中,此加鎖方式仍然會產生問題,感興趣的可以試下是不是

下面我們開始實現一個redis分散式鎖,來避免這種情況發生,先說說實現思路:

1,線程請求訪問前先調用加鎖的方法,加鎖就去里生成一個隨機數同時保存在線程本地變數和redis的某key中,此key設有效期為200ms,具體值根據業務執行時間自行調整,加鎖成功;

2.其它線程試著訪問拿出它本地變數與redis中某key進行比較,如果不一致,則說明有鎖,此線程休眠一段時間,再試著加鎖;

3.加鎖成功的線程在操作結束後刪掉它持有鎖(用lua實現,保證原子性,在它比對和刪除鎖的過程中,其它線程不會加鎖成功),讓其它線程再次加鎖以執行任務;

說明:鎖的時間為200ms可預防線程掛掉之後死鎖,200ms後會自動釋放

下面看看我們寫的鎖代碼:

片段1:使用redislock 實現lock來複寫它的方法

片段2:試著加鎖的方法

片段3:解鎖方法,此處首先從線程本地變數獲取它的隨機數,然後調用lua腳本,與redis中key相比較,如果相同則刪除,否則返回0;

此為lua腳本方法,用此方法可以保證判斷和刪除的原子性,在此過程中沒有線程可以操作此key

到此為止,我們鎖基本寫完,來測試下有沒有用:

我們在此方法前後分別加入加鎖和解鎖方法,使用方式和lock鎖一樣, 我們重新把年齡恢復到30後來測試一下吧

先看看日誌

這裡可以看到各個線程爭奪鎖的情況,再看看執行結果

這裡我們可以看到雖然是32個線程並發執行,但此值並不會變為負數,加鎖成功.

我們可以看到最後2個線程並沒有執行方法

此時說明加鎖成功,大家可以在分散式環境中測試更明顯,有關極端情況下解鎖失敗後應該做什麼也可以由我們自己決定,比redission要靈活,帶鎖的redis最好是單實例,在集群中可能會出問題,有機會我們再用zk實現下.

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 java高級開發 的精彩文章:

TAG:java高級開發 |