能夠先看下以前寫的實現分佈式鎖的方案redis
分佈式鎖的實現數據庫
而後再來看下下面的總結。服務器
redis、數據庫等實現的分佈式鎖,須要設置鎖超時時間的緣由在於:其餘客戶端沒法得知已經獲取鎖的客戶端的狀態 是掛了呢,仍是正在執行。因此只能傻傻的設置一個超時,認爲超時以後就簡單的斷定獲取鎖的客戶端掛了。微信
一旦鎖設定了超時時間,可能獲取鎖的客戶端因各類緣由執行業務操做的時候耗時較長,超出了鎖的超時時間,這時其餘客戶端就能夠再次獲取鎖了,因此就會帶來併發問題。網絡
爲了消除這個鎖超時,就須要由服務器來做爲代理來通知,session
如ZooKeeper,一旦客戶端掛了,就會刪除對應的臨時節點,而後通知watch該節點的其餘客戶端。因此客戶端不須要設置鎖超時,就等待通知便可。架構
從這點來講ZooKeeper是更可靠的,下降了因鎖超時帶來的併發問題。併發
redis、數據庫等方案要想實現高可用,則必須有對應的高可用方案。如最簡單的主從架構,又引入了一致性的問題,又會有不少的坑。分佈式
ZooKeeper方案自己能夠作到高可用、一致性,因此ZooKeeper方案也更簡單一些。佈局
這個單點不是說redis或zookeeper的單點問題,而是客戶端和服務器端的這個鏈接的單點問題。先來舉個例子:
如ZooKeeper仍是會出現併發問題的,如客戶端獲取到鎖了以後,和ZooKeeper鏈接出現了session超時, 就會致使ZooKeeper集羣刪除對應的臨時節點,其餘客戶端也就能獲取到鎖了,此時就存在併發問題。
這種問題的根由就是:客戶端和ZooKeeper集羣之間的鏈接是單鏈接,即只鏈接其中的一臺機器。一旦該鏈接出現網絡抖動, 這種分佈式鎖方案也會出現併發問題。
減小併發的措施:增大session的超時時間,儘可能減小網絡抖動,可是這也會下降服務器端對客戶端的狀態檢測的靈敏度,這個靈敏度在分佈式鎖的場景下也不是特別重要,因此無所謂了。
要消除單點,必然是創建多鏈接來防止網絡的抖動,即客戶端鏈接多個服務器端,向每一個服務器都執行獲取鎖的操做。
如redis的Redlock實現的分佈式鎖。
有N個獨立的master服務器,客戶端會向全部的服務器發送獲取鎖的操做。過半的服務器都獲取到鎖了則認爲獲取到鎖了,這種也有不少細節。這種方式就解決了上述所說的ZooKeeper單鏈接可能形成的併發問題。
然而redis因爲上述1所說的redis自身設計的問題,Redlock實現的分佈式鎖也會有鎖超時問題,即也會存在併發。
因此理想中更好的方案就是:解決了上述2個問題,從而來進一步減小併發的可能性。
redis若是能像ZooKeeper同樣,實現了和客戶端綁定的臨時key,一旦redis客戶端掛了,臨時key刪除,通知watch該key的其餘客戶端(感受這個是一個不錯的需求,不知redis將來是否要實現),就能夠消除鎖超時,再使用Redlock實現的分佈式鎖,這時候可靠性就更高了。
本文側重總結在可靠性方面的問題,性能嘛,單機的redis固然是最快的了,其次zookeeper,最後數據庫。而上述第五點,Redlock方案犧牲了一些性能來換取了可靠性。
其實要解決2個高可用的問題:
數據存儲的高可用(解決基本使用)
如使用redis、數據庫、ZooKeeper,他們承載着分佈式鎖須要的數據,不能是單點的,要集羣高可用
鏈接的高可用(下降併發的機率)
那就須要創建多鏈接,如向N個redis master創建鏈接,向每個都獲取鎖。
因此應該理想的佈局是:
和N個獨立的服務器(如ZooKeeper)都創建鏈接,向每臺服務器都請求獲取鎖的操做,過半成功才表示獲取到鎖
這N個獨立的服務器既有數據的保障,又有多鏈接的保障。因此簡單來講,應該和3個獨立的ZooKeeper機器都創建鏈接,而不是這3臺構成一個ZooKeeper集羣。
歡迎繼續來討論,越辯越清晰。
歡迎關注微信公衆號:乒乓狂魔