針對分布式鎖的實作,目前比較常用的有以下幾種方案:
基于資料庫實作分布式鎖 基于緩存(redis,memcached,tair)實作分布式鎖 基于Zookeeper實作分布式鎖
在分析這幾種實作方案之前我們先來想一下,我們需要的分布式鎖應該是怎麼樣的?(這裡以方法鎖為例,資源鎖同理)
可以保證在分布式部署的應用叢集中,同一個方法在同一時間隻能被一台機器上的一個線程執行。 這把鎖要是一把可重入鎖(避免死鎖) 這把鎖最好是一把阻塞鎖(根據業務需求考慮要不要這條) 有高可用的擷取鎖和釋放鎖功能 擷取鎖和釋放鎖的性能要好
要實作分布式鎖,最簡單的方式可能就是直接建立一張鎖表,然後通過操作該表中的資料來實作了。
當我們要鎖住某個方法或資源時,我們就在該表中增加一條記錄,想要釋放鎖的時候就删除這條記錄。
相比較于基于資料庫實作分布式鎖的方案來說,基于緩存來實作在性能方面會表現的更好一點。而且很多緩存是可以叢集部署的,可以解決單點問題。
目前有很多成熟的緩存産品,包括Redis,memcached以及我們公司内部的Tair。以上實作方式同樣存在幾個問題:
1、這把鎖沒有失效時間,一旦解鎖操作失敗,就會導緻鎖記錄一直在tair中,其他線程無法再獲得到鎖。 2、這把鎖隻能是非阻塞的,無論成功還是失敗都直接傳回。 3、這把鎖是非重入的,一個線程獲得鎖之後,在釋放鎖之前,無法再次獲得該鎖,因為使用到的key在tair中已經存在。無法再執行put操作。
當然,同樣有方式可以解決。
沒有失效時間?tair的put方法支援傳入失效時間,到達時間之後資料會自動删除。
非阻塞?while重複執行。
非可重入?在一個線程擷取到鎖之後,把目前主機資訊和線程資訊儲存起來,下次再擷取之前先檢查自己是不是目前鎖的擁有者。
但是,失效時間我設定多長時間為好?如何設定的失效時間太短,方法沒等執行完,鎖就自動釋放了,那麼就會産生并發問題。如果設定的時間太長,其他擷取鎖的線程就可能要平白的多等一段時間。這個問題使用資料庫實作分布式鎖同樣存在
可以使用緩存來代替資料庫來實作分布式鎖,這個可以提供更好的性能,同時,很多緩存服務都是叢集部署的,可以避免單點問題。并且很多緩存服務都提供了可以用來實作分布式鎖的方法,比如Tair的put方法,redis的setnx方法等。并且,這些緩存服務也都提供了對資料的過期自動删除的支援,可以直接設定逾時時間來控制鎖的釋放。
使用緩存實作分布式鎖的優點
性能好,實作起來較為友善。
使用緩存實作分布式鎖的缺點
通過逾時時間來控制鎖的失效時間并不是十分的靠譜。
基于zookeeper臨時有序節點可以實作的分布式鎖。
大緻思想即為:每個用戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節點的目錄下,生成一個唯一的瞬時有序節點。 判斷是否擷取鎖的方式很簡單,隻需要判斷有序節點中序号最小的一個。 當釋放鎖的時候,隻需将這個瞬時節點删除即可。同時,其可以避免服務當機導緻的鎖無法釋放,而産生的死鎖問題。
來看下Zookeeper能不能解決前面提到的問題。
鎖無法釋放?使用Zookeeper可以有效的解決鎖無法釋放的問題,因為在建立鎖的時候,用戶端會在ZK中建立一個臨時節點,一旦用戶端擷取到鎖之後突然挂掉(Session連接配接斷開),那麼這個臨時節點就會自動删除掉。其他用戶端就可以再次獲得鎖。
非阻塞鎖?使用Zookeeper可以實作阻塞的鎖,用戶端可以通過在ZK中建立順序節點,并且在節點上綁定監聽器,一旦節點有變化,Zookeeper會通知用戶端,用戶端可以檢查自己建立的節點是不是目前所有節點中序号最小的,如果是,那麼自己就擷取到鎖,便可以執行業務邏輯了。
不可重入?使用Zookeeper也可以有效的解決不可重入的問題,用戶端在建立節點的時候,把目前用戶端的主機資訊和線程資訊直接寫入到節點中,下次想要擷取鎖的時候和目前最小的節點中的資料比對一下就可以了。如果和自己的資訊一樣,那麼自己直接擷取到鎖,如果不一樣就再建立一個臨時的順序節點,參與排隊。
單點問題?使用Zookeeper可以有效的解決單點問題,ZK是叢集部署的,隻要叢集中有半數以上的機器存活,就可以對外提供服務。
使用ZK實作的分布式鎖好像完全符合了本文開頭我們對一個分布式鎖的所有期望。但是,其實并不是,Zookeeper實作的分布式鎖其實存在一個缺點,那就是性能上可能并沒有緩存服務那麼高。因為每次在建立鎖和釋放鎖的過程中,都要動态建立、銷毀瞬時節點來實作鎖功能。ZK中建立和删除節點隻能通過Leader伺服器來執行,然後将資料同不到所有的Follower機器上。
其實,使用Zookeeper也有可能帶來并發問題,隻是并不常見而已。考慮這樣的情況,由于網絡抖動,用戶端可ZK叢集的session連接配接斷了,那麼zk以為用戶端挂了,就會删除臨時節點,這時候其他用戶端就可以擷取到分布式鎖了。就可能産生并發問題。這個問題不常見是因為zk有重試機制,一旦zk叢集檢測不到用戶端的心跳,就會重試,Curator用戶端支援多種重試政策。多次重試之後還不行的話才會删除臨時節點。(是以,選擇一個合适的重試政策也比較重要,要在鎖的粒度和并發之間找一個平衡。)
使用Zookeeper實作分布式鎖的優點
有效的解決單點問題,不可重入問題,非阻塞問題以及鎖無法釋放的問題。實作起來較為簡單。
使用Zookeeper實作分布式鎖的缺點
性能上不如使用緩存實作分布式鎖。 需要對ZK的原理有所了解。
上面幾種方式,哪種方式都無法做到完美。就像CAP一樣,在複雜性、可靠性、性能等方面無法同時滿足,是以,根據不同的應用場景選擇最适合自己的才是王道。
資料庫 > 緩存 > Zookeeper
Zookeeper >= 緩存 > 資料庫
緩存 > Zookeeper >= 資料庫
Zookeeper > 緩存 > 資料庫
摘自:http://www.hollischuang.com/archives/1716
本文轉自張昺華-sky部落格園部落格,原文連結:http://www.cnblogs.com/bonelee/p/6430754.html,如需轉載請自行聯系原作者