筆名: haibiscuit
博客園: https://www.cnblogs.com/haibiscuit/
Git地址: https://github.com/haibiscuit?tab=repositories (歡迎star)
本項目地址: https://github.com/haibiscuit/StudyBook
尊重筆者的勞動成果,未經允許請不要轉載
前言:
剛面試了一場,理所當然的掛了,故寫此文給自己壓壓驚,順暢一下我這委屈的心靈.
正文:
一: 分布式鎖面臨的問題
1.1 鎖需要具備唯一性
1.2 鎖需要有超時時間,防止死鎖
1.3 鎖的創建和設置鎖超時時間需要具備原子性
1.4 鎖的超時問題
1.5 鎖的可重入問題
1.6 集群下分布式鎖的問題
1.7 redis分布式鎖需要考慮的其他問題
二: 分布式鎖面臨問題的講解和解決方案
2.1 鎖需要具備唯一性
問題講解:
首先分布式鎖要解決的問題就是分布式環境下同一資源被多個進程進行訪問和操作的問題,既然是同一資源,那么肯定要考慮數據安全問題.其實和單進程下加鎖解鎖的原理是一樣的,單進程下需要考慮多線程對同一變量進行訪問和修改問題,為了保證同一變量不被多個線程同時訪問,按照順序對變量進行修改,就要在訪問變量時進行加鎖,這個加鎖可以是重量級鎖,也可以是基於cas的樂觀鎖.
解決方案:
使用redis命令setnx(set if not exist),即只能被一個客戶端占坑,如果redis實例存在唯一鍵(key),如果再想在該鍵(key)上設置值,就會被拒絕.
2.2 鎖需要有超時時間,防止死鎖
問題講解:
redis釋放鎖需要客戶端的操作,如果此時客戶端突然掛了,就沒有釋放鎖的操作了,也意味着其他客戶端想要重新加鎖,卻加不了的問題.
解決方案:
所以,為了避免客戶端掛掉或者說是客戶端不能正常釋放鎖的問題,需要在加鎖的同時,給鎖加上超時時間.
即,加鎖和給鎖加上超時時間的操作如下操作:
>setnx lockkey true #加鎖操作
ok
>expire lockkey 5 #給鎖加上超時時間
... do something critical ...
>del lockkey #釋放鎖
(integer) 1
2.3 鎖的創建和設置鎖超時時間需要具備原子性
問題講解:
通過2.3加鎖和超時時間的設置可以看到,setnx和expire需要兩個命令來完成操作,也就是需要兩次RTT操作,如果在setnx和expire兩次命令之間,客戶端突然掛掉,這時又無法釋放鎖,且又回到了死鎖的問題.
解決方案:
使用set擴展命令
如下:
>set lockkey true ex 5 nx #加鎖,過期時間5s
ok
... do something critical ...
>del lockkey
以上的set lockkey true ex 5 nx命令可以一次性完成setnx和expire兩個操作,也就是解決了原子性問題.
2.4 鎖的超時問題
問題講解:
雖然上面給鎖加上了超時時間,但是客戶端並不能一定在超時時間之內完成定時任務,所以,即使當前客戶端沒有完成任務,此時又會有其他的客戶端設置鎖成功,此時同一資源將會面臨多個客戶端同時操作的問題.
解決方案:
客戶端可以在鎖設置成功之后,進行定時任務,在鎖超時之前使用lua腳本刪除鎖並重新設置鎖和超時時間.
當然,這里為什么會使用lua來完成操作呢,其實和上面的原子性問題一樣,在刪除鎖和重新設置鎖和鎖的超時時間之間,可能面臨其他的客戶端將鎖資源占有,而lua具有原子性的特性,刪除鎖和重新加鎖這兩個操作要么都完成,要么都不完成.
2.5 鎖的可重入問題
問題講解:
上面我們講了,為了保證鎖具有唯一性,需要使用setnx,后來為了與超時時間一起設置,我們選用了set命令.
在我們想要在加鎖期間,擁有鎖的客戶端想要再次獲得鎖,也就是鎖重入,當然,這里的問題和2.5問題類似.
解決方案:
同樣,我們可以選擇使用lua腳本的方案,將鎖重新刪除和設置.
2.6 集群下分布式鎖的問題
問題講解:
這一問題是在redis集群方案時會出現的.事實上,現在為了保證redis的高可用和訪問性能,都會設置redis的主節點和從節點,主節點負責寫操作,從節點負責讀操作,也就意味着,我們所有的鎖都要寫在主redis服務器實例中,如果主redis服務器宕機,資源釋放(在沒有加持久化時候,如果加了持久化,這一問題會更加復雜),此時redis主節點的數據並沒有復制到從服務器,此時,其他客戶端就會趁機獲取鎖,而之前擁有鎖的客戶端可能還在對資源進行操作,此時又會出現多客戶端對同一資源進行訪問和操作的問題.
解決方案:
這一問題准備單獨成文進行討論,因為並不是一兩句話可以說清楚.
2.7 redis分布式鎖需要考慮的其他問題
以上討論了redis在業務邏輯上一定會遇到的問題,解決方案可能也就局限於我上面所討論的方案.
當然,還有邏輯需要根據程序員自己的場景進行選定.
例如:
(1) 設置鍵時,鍵資源從哪獲取
其中涉及多客戶端的資源共識問題,簡單的分析就是,你怎么確定在多個客戶端對同一資源進行加鎖操作的時候,這個鍵(key)就需要多個客戶端一致,否則,我怎么保證鍵在多個客戶端的唯一性呢.
(2) 釋放鎖時只能由擁有鎖資源的客戶端釋放
當然,這個問題是由於你加鎖和解鎖邏輯寫的不對的情況.根據上面分析,只要解決上面所有對redis分布式鎖的所有問題,這個客戶端鎖釋放的問題一般碰不到.
不過,這也是細節問題,需要將該問題考慮在業務邏輯中
三: 總結
redis分布式鎖在集群下出現的問題,我將會在另外一篇文章中講到
希望自己的工作趕快穩定下來,加油吧,屌絲