1.什么是分布式鎖
為了防止分布式系統中的多個進程之間相互干擾,我們需要一種分布式協調技術來對這些進程調度,而這種分布式協調技術的核心就是分布式鎖。
2.分布式鎖應該具備的條件
- 一個方法在同一時間只能被一個機器的一個線程執行。
- 高可用的獲取鎖與釋放鎖。
- 高性能的獲取鎖與釋放鎖。
- 具備可重入特性。
- 具有鎖失效機制,防止死鎖。
- 具備非阻塞特性,即沒有獲取鎖將直接返回獲取鎖失敗。
3.redis實現分布式鎖
分布式鎖實現的三個核心要素:加鎖、解鎖、鎖超時。
(1)加鎖
以雙11秒殺商品為例討論。小明對商品A進行購買操作,此時小明會使用 setnx(prodId,threadId) 命令,來對商品加鎖。若此時執行命令之后返回 1 ,則說明小明加鎖成功。小紅在同一時間做相同操作,返回 0 ,則說明小紅加鎖失敗。
(2)解鎖
小明購買完商品之后要立即釋放鎖,提供給包括小紅在內的其他購買者使用,小明此時執行del(prodId)命令,將鎖釋放。
(3)鎖超時
如果小明在購買的過程中,突然網斷了,獲取了鎖,但是沒有釋放鎖,那之后的購買者只有等待。所以為了解決這種 死鎖 的問題,小明在加鎖時,必須執行 expire(prodId,30) 給鎖設置一個超時時間,來保證即使沒有顯示的釋放鎖,這把鎖在一定時間之后也能自動釋放。
偽代碼如下:
if(setnx(prodId,threadId) == 1){ expire(prodId,30) try { do something ...... } finally { del(prodId) } }
4.以上偽代碼中存在三個致命問題
(1) setnx 和 expire 的非原子性
setnx 與 expire 是分兩步進行操作的,如果執行完setnx操作之后,還沒執行 expire 命令設置超時時間,網絡斷掉,此時造成死鎖。
出現這個問題的根本原因在於setnx與expire是分兩步進行的,如果這兩個操作時原子性的,將很好的避免這種問題,所以redis提供了解決方案:在加鎖的同時,設置超時時間。
set(prodId,threadId,30)
(2) 誤刪操作
小明在購買商品加鎖時設置的鎖超時時間時30s,但是30s之后小明仍然沒有購買完成,此時redis自動釋放鎖,小紅獲得了鎖,在小紅購物的過程中,小明購買完成,需要刪除鎖,誤將小紅的鎖刪除。
為了避免這個問題,在加鎖時,小明需要將自己的線程Id當作參數傳進去。
String threadId = Thread.currentThread().getId() set(key,threadId ,30)
在解鎖時先判斷是不是自己的鎖,如果是自己的鎖,再進行刪除鎖的操作。
if(threadId .equals(redisClient.get(key))){ del(key) }
(3)同一時間兩個線程訪問同一資源
上述情況會出現同一時間兩個線程訪問同一資源的情況。為了盡量避免這種情況,我們可以給獲得鎖的線程去開啟一個守護進程,當到達超時時間時,如果還沒有進行完自己的操作,守護進程,給該進程加部分時間,當該線程處理完自己的任務之后,會顯示的關閉掉守護進程。如果沒有處理完成,斷網了,守護進程會自動終止,到達超時時間時,該線程就將鎖釋放了。
5.Zookeeper如何實現分布式鎖
(1)四種Zonde節點
- 持久節點
默認的節點類型。創建節點的客戶端與Zookeeper斷開連接之后,該節點依舊存在。
- 持久順序節點
在創建節點時,zookeeper會根據創建的時間將節點進行排序。
- 臨時節點
和持久節點想相反。當創建節點的客戶端與zookeeper斷開連接之后,臨時節點會被刪除。
- 臨時順序節點
當創建節點時,zookeeper會根據節點的創建時間的先后順序進行排序,當創建節點的客戶端與Zookeeper斷開連接之后,臨時節點會被刪除。
(2)zookeeper 分布式鎖原理
zookeeper實現分布式鎖就是應用了臨時順序節點,具體流程如下:
① 100名客戶在極短的時間內去訪問商品服務,進行購買商品的操作。
② 獲取鎖
zookeeper創建100個臨時順序節點,這些節點按照請求的先后順序進行排序。排名第一的 req1 優先獲取鎖,排名第二 req2 的向只比他靠前一名的 req1 注冊Watcher,排名第三的 req3 向 req2 注冊Watcher,依次類推。
③ 釋放鎖
◉正常執行完釋放鎖
當 req1 執行完成之后,zookeper會刪除該節點,排名第二的 req2 獲得鎖,依次類推。
◉客戶端崩潰釋放鎖
當排名第一的用戶在購買時斷網,因為zookeeper為其創建的是臨時順序節點,此時排名第一的用戶獲得的鎖也將釋放。排名第二的用戶獲得鎖。
6.redis與zookeeper的分布式鎖比較
分布式鎖 | 優點 | 缺點 |
zookeeper | 1.有封裝好的框架,容易實現 2.有等待鎖的隊列,大大提高搶鎖效率
|
添加和刪除節點性能較低 |
redis | set與del命令性能高 | 實現復雜,沒有等待隊列,容易出現羊群效應 |