分佈式鎖之Redis鎖和ZK鎖

分佈式鎖

分佈式系統中,常見的分佈式鎖有兩種,一種是基於Redis實現的分佈式鎖,一種是基於ZooKeeper鎖。本篇文章簡要介紹下其原理及方案。redis

Redis鎖

redis鎖簡單版本

上鎖

先說上鎖的命令,上鎖的命令是:set {lockName} {randomVal} nx px 30000。spring

其中,nx 參數的意思是不存在鎖的時候設置,px參數表示毫秒數,該條命令表示當不存在lockName鍵的時候,爲其設置值爲randomVal,並設置過時時間爲30000毫秒。當redis中存在該鍵是redis返回nil。其餘線程(包括其餘機器)來獲取鎖的時候,能夠用輪詢來判斷是否上鎖成功,達到阻塞其餘線程的目的。網絡

解鎖

解鎖,咱們須要實現的需求是不能刪除掉其餘線程設置的鎖。由於某些狀況好比鎖超時,其餘線程仍是會獲取到鎖。因此必須先判斷鎖是否是本身設置的再進行刪除,因爲redis沒有提供一個原子命令判斷當前值是什麼再進行刪除,因此必須向redis傳入lua腳本以確保解鎖操做的原子性。解鎖的原理是傳入隨機值進行解鎖,腳本中會判斷當前Key存不存在,存在的話再判斷值是否與傳入的隨機值相等,相等則將其刪除。架構

缺點

這種上鎖方案有很明顯的的缺點,如:併發

  • 一是這種方案並非很可靠,被上鎖的redis宕機後容易丟數據,就算是配置了哨兵,也存在主備切換的時候可能丟數據。
  • 二是不能實現公平鎖。
  • 三是輪詢阻塞這種方式開銷有點大。
  • 四是不可實現線程重入。
  • 沒有續約機制

針對第一點,redis官方建議使用基於redisCluster的redlock(紅鎖)方案。這種方案核心要點就是須要在大多數redis節點上獲取鎖成功纔算成功。但這就意味着開銷變大了,而且針對紅鎖這種方案,網絡上也有些大佬們提出質疑。框架

解決方案

這種簡單版本的redis分佈式鎖方案並不能解決這些問題,若是要解決可使用redission框架,redission運用了隊列、redis發佈訂閱機制,看門狗機制,較爲複雜的加鎖、釋放鎖腳本解決了這些問題。redission支持可重入鎖、公平鎖、紅鎖等,而且將redis鎖按JDK的Lock接口進行了封裝,操做簡單易用。dom

ZK鎖

簡單版本的不公平ZK鎖

上鎖

以在ZooKeeper中成功建立臨時節點爲標識,建立成功則獲取鎖成功,建立失敗則監聽該節點,直到節點刪除在嘗試建立臨時節點。 爲何使用臨時節點?避免服務宕機,致使死鎖問題。分佈式

解鎖

刪除節點即解鎖成功。lua

方案缺點

這種方案的缺點是鎖是不公平的,而且節點刪除喚醒的其餘監聽線程比較多,效率沒有接下來介紹的使用臨時順序節點的方案只喚醒下一個監聽節點的方式高。線程

基於臨時順序節點的公平ZK鎖

上鎖

每次嘗試獲取鎖都嘗試建立一個臨時順序節點,而且獲取當且父節點下的全部臨時順序節點,若是前面還有節點,則獲取鎖不成功,此時將主線程阻塞,監聽前面一個節點被刪除,若是被刪除再喚醒主線程。反之若是當前建立的臨時順序節點前面沒有節點則獲取鎖成功。

解鎖

刪除當前臨時順序節點即解鎖成功。

解決的問題

這種方案實現的是公平鎖,之前的併發競爭ZK臨時節點建立,改成依次喚醒,下降了必定開銷。

兩種方案的對比

我的以爲對於分佈式系統來講,redisCluster紅鎖的設計不是很優雅,感受基於zookeeper集羣高可用的zk鎖更優雅一些。因此若是作技術選型的話,我的傾向zk鎖。可是若是技術架構中沒有搭建zookeeper,可能選擇的是springcloud那一套,選擇redisssion封裝的redis鎖也行。

相關文章
相關標籤/搜索