分佈式鎖:ZooKeeper與Redis的區別

分佈式鎖:ZooKeeper與Redis的區別

在分佈式系統中,分佈式鎖的應用場景是非常廣泛的。Redis和ZooKeeper是目前比較常見的實現方案,那它們之間有什麼區別呢,我們應該如何選擇?

對於ZooKeeper來說,它的會話與服務端是通過心跳保持連接的,當心跳超時客戶端會收到鏈接丟失的事件,通常來說這不是問題,因為ZK的客戶端對自動連接。但如果一直連接不上,服務端會在會話有效期之後,將會話置為過期。這裡有個很重要的細節,即使會話已經過期了,在重新連接上服務器之前,客戶端永遠也不知道自己已經過期了,因為ZooKeeper的會話過期是由服務器端觸發並通知客戶端的。

這樣,如果客戶端a與ZooKeeper服務器之間的通信長時間斷開的話,客戶端a會誤以為自己並沒有過期,只是臨時性的鏈接丟失,但實際上服務端有可能已經將它置為過期,而這時其它與ZooKeeper服務器通信正常的客戶端就有可能獲取同一個鎖(因為服務端在將客戶端a置過期的同時會清除它所創建的臨時節點),進而造成兩個客戶端同時訪問臨界區的資源。

要解決這個問題也不難,在鏈接丟失和重新建立鏈接時,客戶端都會收到相應的事件通知。我們可以在客戶端維護一個狀態變量(比如valid),在鏈接丟失時將它置為false,並在重新建立鏈接後將它置為true,並在客戶端訪問臨界區的資源代碼中不定期的檢查valid的值,一旦發生valid值變為false時,就說明鏈接丟失了,這時就暫停臨界區資源的訪問,並等待重新建立鏈接。

這麼做挺麻煩的,大大增加了使用鎖的複雜性。但是否真需要這麼玩也是看具體的場景的:

  1. 如果客戶端加鎖成功後,需要訪問的資源本身是依賴於ZooKeeper服務器的通知的話,那我們就沒必要繞這麼一個彎路。比如ZooKeeper實現的主-從模式,成功創建master節點的客戶端成為master節點,而它的主要作用是監控ZooKeeper的其它節點,進行相應的任務分配。如果發生了鏈接丟失,在重新建立鏈接前當前的master不可能接收到來身ZooKeeper服務器的任何通知,即使別的客戶端在這時成功了新的master也不會產生問題,因為鏈接丟失的客戶端在這種情況下重新鏈接後會收到會話過期事件。
  2. 如果客戶端加鎖成功後,需要訪問的資源不依賴於ZooKeeper服務,那我們就不得不繞這麼一個彎路了,原因已經在上面分析過了。

使用Redis實現的鎖,並不存在這樣的問題,因為key並不會因為客戶端怎麼樣而被刪除。但它也有麻煩的一面,為了防止客戶端長時間阻塞或者故障宕機而導至鎖無法釋放,我們需要在加鎖的時候指定一個過期時間,不過成本確實比ZooKeeper的實現要低很多。

經過以上分析,我們不難得出以下結論:分佈式鎖的實現使用ZooKeeper還是redis來實現,取決於加鎖的目的。

  1. 如果加鎖成功後要做的事情,是一個長時性的或者持續性的工作,那麼使用ZooKeeper實現應該會更合理,因為我們很難確定過期時間的設置。
  2. 如果加鎖成功後要訪問的資源,可以在很短的時間內完成(豪秒級或秒級),那麼使用Redis更合理,複雜度更低,過期時間的設置也容易。


分享到:


相關文章: