現在很多專案上都會使用redis來實現分布式鎖,相比zk無論是使用還是理解都比較容易,但也是出現了一些五花八門的使用方式,漏洞百出,本文主要介紹一下在redis單機未部署集群環境下,都出現了哪些錯誤使用方式。
直接上**
("/redislock"
)public string redislock()
throws interruptedexception
else
}這裡使用spring提供的redistemplate操作redis,setifabsent實際上底層執行的是setnx。
@override
public boolean setifabsent
(k key, v value)
加鎖與設定鎖的過期時間非原子操作,也就是有可能加鎖成功了,但是設定過期時間時失敗了,這就造成了鎖永遠不會被刪除。
("/redislock"
)public string redislock()
throws interruptedexception
else
}請求發生異常
鎖設定成功了,但是卻沒有失效時間,現在無論再來多少請求都不會再執行業務**了。
通常我們使用原子命令即可。
redistemplate.
opsforvalue()
.setifabsent
("redislock"
,"-1",6
,timeunit.seconds)
@override
public boolean setifabsent
(k key, v value,
long timeout, timeunit unit)
這種問題可能在舊版本時比較常見,因為當時redis就是沒有提供加鎖與設定鎖過期時間的原子set命令。
從官方文件中可以看到這是在2.6.12之後才提供的。
乙個請求加的鎖被另乙個請求釋放。
("/redislock"
)public string redislock
(@requestparam
("time"
)long time,
@requestparam
("userid"
) string userid)
throws interruptedexception
else
}三次請求
輸出結果
簡單分析一下這個問題:
當第乙個請求發出時,業務**需要執行15秒,而鎖在10秒後就過期了,此時第二個請求發出需要執行8秒,而第乙個請求還在繼續執行當15秒到了以後,第乙個執行緒就把鎖刪除了,那麼這次刪除也把第二個請求加的鎖給刪除了,所以就造成了第三個請求在第二個請求執行未結束且鎖也未到期時就執行了。
從圖中可以看出,第乙個請求和第二個請求同時執行了,原因是鎖超時,這還情有可原。
但是第二個請求未超時卻也和第三個請求同時執行了,這種情況就不能接收了。
執行刪除時需要對value值進行校驗,並配合lua指令碼讓校驗的操作保持原子性。主要作用就是不能讓乙個執行緒去刪除另乙個執行緒加的鎖。
("/redislock"
)public string redislock
(@requestparam
("time"
)long time,
@requestparam
("userid"
) string userid)
throws interruptedexception
else
}public
void
unlock
(string key, string value)
現在即使第乙個請求結束了,第三個請求在第二個請求執行完或者到期之前都不會執行了。
分布式鎖 使用Redis實現分布式鎖
關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...
正確方式實現Redis分布式鎖
實現分布式鎖的方式有很多 1 zookeeper 2 redis 3 阿里開源等。但是在redis中進行實現鎖的網上百分之99的案例中,都如下 long i jedis.setnx key,key if i 1 else 這個是這裡在setnx之後還沒來得及設定過期時間就宕機了,這樣會導致deadl...
Memcached 和 Redis 分布式鎖方案
分布式快取,能解決單台伺服器記憶體不能無限擴張的瓶頸。在分布式快取的應用中,會遇到多個客戶端同時爭用的問題。這個時候,需要用到分布式鎖,得到鎖的客戶端才有操作許可權。memcached 和 redis 是常用的分布式快取構建方案,下面列舉下基於memcached 和 redis 分布式鎖的實現方法。...