先聊下redis普通的分布式鎖,用
1.單節點、主從/哨兵模式的分布式鎖,安全嗎?
或許你了解過,通過如下方式加鎖:
設置鎖時,使用set命令,因為其包含了setnx,expire的功能,起到了原子操作的效果,給key設置隨機值,並且只有在key不存在時才設置成功返回True,並且設置key的過期時間(最好用毫秒)
1 SET key_name my_random_value NX PX 30000 # NX 表示if not exist 就設置並返回True,否則不設置並返回False PX 表示過期時間用毫秒級, 30000 表示這些毫秒時間后此key過期
2.在獲取鎖后,並完成相關業務后,需要刪除自己設置的鎖(必須是只能刪除自己設置的鎖,不能刪除他人設置的鎖);
刪除原因:保證服務器資源的高利用效率,不用等到鎖自動過期才刪除;
刪除方法:最好使用Lua腳本刪除(redis保證執行此腳本時不執行其他操作,保證操作的原子性),代碼如下;邏輯是 先獲取key,如果存在並且值是自己設置的就刪除此key;否則就跳過;
1 if redis.call("get",KEYS[1]) == ARGV[1] then 2 return redis.call("del",KEYS[1]) 3 else 4 return 0 5 end
單點Redis鎖的缺陷:這個缺陷其實很明顯,如果只有一個Redis實例,這個掛了,所有依賴他的服務都掛了。顯然不太適合大型的應用。
2.升級為主從架構模式的問題
為了避免單點故障,我們給Redis做一個Master/Slave的主從架構,一個Master,一台Slave。下面就會碰到這么一個問題。下面是使用場景。
- 客戶端A在Master上獲取到一個鎖。
- Master把這個數據同步到Slave的時候掛了(因為Master和Slave之間同步是異步的)。
- Slave變成了Master。
- 客戶端B通過相同的key,和value獲取到鎖。分布式鎖失效
- 釋放鎖的操作,得釋放自己加的鎖。
3.redisRedlock
全名叫做 Redis Distributed Lock;即使用redis實現的分布式鎖; RedLock官網說明 Distributed locks with Redis,英文不好的小伙伴,可以參考這篇文章,坐着翻譯的還不錯 《Redis分布式鎖RedLock》
個人總結如下:
Redlock算法
假設我們有N(假設5)個Redis master實例,所有節點相互獨立,並且業務系統也是單純的調用,並沒有什么其他的類似消息重發之類的輔助系統。下面來模擬一下算法:
- 客戶端獲取服務器當前的的時間t0,毫秒數。
- 使用相同的key和value依次向5個實例獲取鎖。客戶端在獲取鎖的時候自身設置一個遠小於業務鎖需要的持續時間的超時時間。舉個例子,假設鎖需要10秒,超時時間可以設置成比如5-50毫秒。這個避免某個Redis本身已經掛了,但是客戶端一直在嘗試獲取鎖的情況。超時了之后就直接跳到下一個節點。
- 客戶端通過當前時間(t1)減去t0,計算獲取鎖所消耗的時間t2(=t1-t0)。只有t2小於鎖的業務有效時間(也就是第二步的10秒),並且,客戶端在至少3(5/2+1)台上獲取到鎖我們才認為鎖獲取成功。
- 如果鎖已經獲取,那么鎖的業務有效時間為10s-t2。
- 如果客戶端沒有獲取到鎖,可能是沒有在大於等於N/2+1個實例上獲取鎖,也可能是有效時間(10s-t2)為負數,我們就嘗試去釋放鎖,即使是並沒有在那個節點上獲取到。
鎖的釋放
釋放比較簡單,直接刪除所有實例上對應的key就好。確定要釋放的是自己所的value.別釋放了他人的。
以上是RedLock釋放鎖的源碼,用到了
Future模式、Lua腳本,這部分可以自己了解下,看源碼總是頭疼,但是大牛寫的源碼就是這樣。以后會專門寫一篇分析redLock源碼的文章。