背景
redlock算法是為了解決什么問題呢?
在單redis實例實現分布式鎖時,可能會出現線程A設置完鎖后,master掛掉,slave提升為master,因為異步復制的特性,線程A設置的鎖丟失了,這時候線程B設置鎖也能夠成功,導致線程A和B同時擁有鎖
然后redis作者提出了redlock算法
算法描述
-
獲得當前時間(ms)
-
首先設置一個鎖有效時間valid_time,也就是超過這個時間后鎖自動釋放,使用相同的key和value對所有redis實例進行設置,每次鏈接redis實例時設置一個小於valid_time的超時時間,比如valid_time時10s,那超時時間可以設置成50ms,如果這個實例不行,那么換下一個設置
-
計算獲取鎖總共占用的時間,再加上時鍾偏移,如果這個總時間小於valid_time,並且成功設置鎖的實例數>= N/2 + 1,那么加鎖成功
-
如果加鎖成功了,那么這個鎖的有效時間就是valid_time - 獲取鎖占用的時間 - 時鍾偏移
-
如果加鎖失敗,解鎖所有實例(每個redis實例都運行del key)
問題
好了,算法看完了,給我的感覺就是,好像更不靠譜了,因為引入了一個不靠譜的時間,時間偏差在分布式系統中應該是很容易出現的吧,在鎖有效時間里雖然減去了時鍾偏移,但是應該減多少合適呢,要是這個值設置不好,很容易出現問題
先不提這個,我們回顧一下單redis實例的問題,主從切換時可能會有兩個線程占有鎖,那么redlock有沒有這個問題呢
假設我們有5個redis實例a,b,c,d,e,線程A分別加鎖,然后a,b,c加鎖成功,因為網絡分區問題,d,e失敗。同時,線程B也加鎖,因為網絡分區問題,a,b加鎖失敗,但是d,e加鎖成功。這時候,顯然線程A會獲得鎖,但是如果這時候c的master掛了,然后切換成了slave,slave中沒有A加鎖的信息,恰巧這時線程B對c加鎖的命令到了,那線程B就會加鎖成功,這時候,線程A和B都給三個節點加鎖成功了,他們同時擁有鎖。所以我認為redlock仍然沒有解決這個問題,只是讓這個問題更不容易發生了,這里如果我想錯的話歡迎指正
另外,redis作者也對這個算法進行了分析,建議對鎖互斥的安全性要求高的應用不要使用這個算法,分析了GC停頓,page fault,clock jump等等對時間產生的影響,可以參考http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html