先大膽下一個結(jié)論搅轿。
由于分布式環(huán)境的復雜性目前是沒有完美的分布式鎖實現(xiàn)方案的恨锚。無論是基于redis的RedLock還是基于zookeeper的分布式鎖大猛。
這一篇主要討論RedLock
簡介
基于redis集群的分布式鎖??
簡單實現(xiàn)
算法1
1 set lock_key fix_value NX
2 expire lock_key time
問題
a 假設(shè)client A 執(zhí)行到步驟1务蝠,然后client A掛掉匹耕,那么永遠無法執(zhí)行到步驟2了渔呵,也就是說形成了死鎖-安全性問題
b client A 執(zhí)行完整個步驟怒竿,還未進入臨界區(qū),然后因為gc停頓或者其他原因停頓扩氢,然后鎖自動過期了耕驰;client B執(zhí)行完整個步驟,拿到鎖录豺,此時client A和client B都獲得了??朦肘;更加嚴重的是,client A從阻塞狀態(tài)恢復了巩检,會去釋放掉??厚骗,導致B的??也被釋放了-安全性問題
c 單節(jié)點的redis存在掛掉的情況-活性問題
d 主備形式的redis集群,當主掛掉之后兢哭,備會頂上领舰,但是redis的異步復制的,備頂上可能此時還沒有主的數(shù)數(shù)據(jù)-安全性問題
初步解決
a 將步驟1和步驟2合并成一個原子操作
算法2
set lock_key fix_value NX PX time
b 將fix_value 換成 random value
釋放鎖的操作變成如下
算法3
unlock(key, value){
if(redis.call(“get”, key) == value) then
return redis.call(“del”, key)
else
return 0
}
上述操作是利用lua完成迟螺,為原子操作冲秽,含義為先查看下lock對應的value是否還是自己set進去的值,如果是則可以釋放鎖矩父,否則認為鎖已經(jīng)被別人占用
上述方式還是無法解決b的第一個問題锉桑,但是可以解決b的第二個問題
c 和 d的問題需要完成巨大的變更才有可能解決
RedLock
算法描述
基于多master的redis集群,保證在大多數(shù)節(jié)點存活的情況下鎖的活性和安全性問題
定義
a 鎖的超時時間timeout-client需要在這個給定時間內(nèi)獲取到鎖窍株,否則認為是超時的
b 鎖的有效時間lock validity time-client獲得鎖后民轴,在這個時間段后需要釋放鎖
a是遠小于b的
c 鎖節(jié)點的個數(shù)
加鎖過程
1 記錄下當前時間戳
2 依次向redis集群中執(zhí)行算法2的操作,對于單個節(jié)點來說球订,如果在a的時間內(nèi)獲得??后裸,則認為獲得此節(jié)點的鎖,反之亦然冒滩。
3 判斷是否獲得大多數(shù)??微驶,并將此時的時間戳減去步驟1的時間戳得到b1,如果b1大于b的時間开睡,則認為獲取失敗因苹,觸發(fā)釋放鎖邏輯,否則認為獲取鎖成功篇恒,鎖的時間長度為b的時間減去b1的時間長度扶檐。
釋放鎖過程
依次向每個redis節(jié)點發(fā)出算法3的邏輯
問題分析
1 b的第一個問題還是無法解決
2 由于時鐘跳躍會引發(fā)新的問題,某個節(jié)點已經(jīng)被client獲取了鎖婚度,但是因為時鐘跳躍問題蘸秘,鎖迅速的過期了官卡,那么可能存在另一個client來獲取鎖時,此節(jié)點會將鎖分配給他醋虏,那么等于說一個節(jié)點被兩個client同時成功獲取鎖了寻咒。
Martin認為
出于效率來使用分布式鎖,允許偶爾的??失敗颈嚼,僅僅需要簡單的分布式鎖實現(xiàn)方案就可以了毛秘,RedLock的實現(xiàn)偏重
出于正確性來使用分布式鎖,redLock會因為這樣那樣的分布式問題阻课,而無法達到正確性的要求