一文弄懂“分散式鎖”,一直以來你的選擇依據正確嗎?
本文主要會關注的問題是“分散式鎖”的問題。
多執行緒情況下對共享資源的操作需要加鎖,避免資料被寫亂,在分散式系統中,這個問題也是存在的,此時就需要一個分散式鎖服務。
常見的分散式鎖實現一般是基於DB、Redis、Zookeeper。下面筆者會按照順序分析下這3種分散式鎖的設計與實現,想直接看分散式鎖總結的小夥伴可直接翻到文件末尾處。
分散式鎖的實現由多種方式,但是不管怎樣,分散式鎖一般要有以下特點:
排他性: 任意時刻,只能有一個client能獲取到鎖;
容錯性: 分散式鎖服務一般要滿足AP,也就是說,只要分散式鎖服務叢集節點大部分存活,client就可以進行加鎖解鎖操作;
避免死鎖: 分散式鎖一定能得到釋放,即使client在釋放之前崩潰或者網路不可達。
除了以上特點之外,分散式鎖最好也能滿足可重入、高效能、阻塞鎖特性(AQS這種,能夠及時從阻塞狀態喚醒)等,下面就話不多說,趕緊上(開往分散式鎖的設計與實現的)車。
一、DB鎖
在資料庫新建一張表用於控制併發控制,表結構可以如下所示:
key_id作為分散式key用來併發控制,memo可用來記錄一些操作內容(比如memo可用來支援重入特性,標記下當前加鎖的client和加鎖次數)。將key_id設定為唯一索引,保證了針對同一個key_id只有一個加鎖(資料插入)能成功。此時lock和unlock虛擬碼如下:
注意,虛擬碼中的lock操作是非阻塞鎖,也就是tryLock,如果想實現阻塞(或者阻塞超時)加鎖,只修反覆執行lock虛擬碼直到加鎖成功為止即可。
基於DB的分散式鎖其實有一個問題,那就是如果加鎖成功後,client端當機或者由於網路原因導致沒有解鎖,那麼其他client就無法對該key_id進行加鎖並且無法釋放了。為了能夠讓鎖失效,需要在應用層加上定時任務,去刪除過期還未解鎖的記錄,比如刪除2分鐘前未解鎖的虛擬碼如下:
因為單例項DB的TPS一般為幾百,所以基於DB的分散式效能上限一般也是1k以下,一般在併發量不大的場景下該分散式鎖是滿足需求的,不會出現效能問題。
不過DB作為分散式鎖服務需要考慮單點問題,對於分散式系統來說是不允許出現單點的,一般透過資料庫的同步複製,以及使用vip切換Master就能解決這個問題。
以上DB分散式鎖是透過insert來實現的,如果加鎖的資料已經在資料庫中存在,那麼用select xxx where key_id = xxx for udpate方式來做也是可以的。
二、Redis鎖
Redis鎖是透過以下命令對資源進行加鎖:
set key_id key_value NX PX expireTime
其中,set nx命令只會在key不存在時給key進行賦值,px用來設定key過期時間,key_value一般是隨機值,用來保證釋放鎖的安全性(釋放時會判斷是否是之前設定過的隨機值,只有是才釋放鎖)。由於資源設定了過期時間,一定時間後鎖會自動釋放。
set nx保證併發加鎖時只有一個client能設定成功(Redis內部是單執行緒,並且資料存在記憶體中,也就是說Redis內部執行命令是不會有多執行緒同步問題的),此時的lock/unlock虛擬碼如下:
分散式鎖服務中的一個問題
如果一個獲取到鎖的client因為某種原因導致沒能及時釋放鎖,並且Redis因為超時釋放了鎖,另外一個client獲取到了鎖,此時情況如下圖所示:
那麼如何解決這個問題呢?
一種方案是引入鎖續約機制,也就是獲取鎖之後,釋放鎖之前,會定時進行鎖續約,比如以鎖超時時間的1/3為間隔週期進行鎖續約。
關於開源的Redis的分散式鎖實現有很多,比較出名的有redisson、百度的dlock,關於分散式鎖,筆者也寫了一個簡易版的分散式鎖Redis-lock,主要是增加了鎖續約和可同時針對多個key加鎖的機制。
對於高可用性,一般可以透過叢集或者master-slave來解決,Redis鎖優勢是效能出色,劣勢就是由於資料在記憶體中,一旦快取服務當機,鎖資料就丟失了。
像Redis自帶複製功能,可以對資料可靠性有一定的保證,但是由於複製也是非同步完成的,因此依然可能出現master節點寫入鎖資料而未同步到slave節點的時候當機,鎖資料丟失問題。
三、Zookeeper分散式鎖
ZooKeeper是一個高可用的分散式協調服務,由雅虎建立,是Google Chubby的開源實現。
ZooKeeper提供了一項基本的服務:分散式鎖服務。想學習Java工程化、分散式架構、高併發、高效能、深入淺出、微服務架構、Spring,MyBatis,Netty原始碼分析等技術可以加群:479499375,群裡有阿里大牛直播講解技術,以及Java大型網際網路技術的影片免費分享給大家,歡迎進群一起深入交流學習。
Zookeeper重要的3個特徵是:zab協議、node儲存模型和watcher機制。透過zab協議保證資料一致性,Zookeeper叢集部署保證可用性,node儲存在記憶體中,提高了資料操作效能,使用watcher機制,實現了通知機制(比如加鎖成功的client釋放鎖時可以通知到其他client)。
Zookeeper node模型支援臨時節點特性,即client寫入的資料時臨時資料,當客戶端當機時臨時資料會被刪除,這樣就不需要給鎖增加超時釋放機制了。
當針對同一個path併發多個建立請求時,只有一個client能建立成功,這個特性用來實現分散式鎖。注意:如果client端沒有當機,由於網路原因導致Zookeeper服務與client心跳失敗,那麼Zookeeper也會把臨時資料給刪除掉的,這時如果client還在操作共享資料,是有一定風險的。
基於Zookeeper實現分散式鎖,相對於基於Redis和DB的實現來說,使用上更容易,效率與穩定性較好。curator封裝了對Zookeeper的API操作,同時也封裝了一些高階特性,如:Cache事件監聽、選舉、分散式鎖、分散式計數器、分散式Barrier等,使用curator進行分散式加鎖示例如下:
四、總結
從上面介紹的3種分散式鎖的設計與實現中,我們可以看出每種實現都有各自的特點,針對潛在的問題有不同的解決方案,歸納如下:
效能: Redis > Zookeeper > DB。
避免死鎖: DB透過應用層設定定時任務來刪除過期還未釋放的鎖,Redis透過設定超時時間來解決,而Zookeeper是透過臨時節點來解決。
可用性: DB可透過資料庫同步複製,vip切換master來解決;Redis可透過叢集或者master-slave方式來解決;Zookeeper本身自己是透過zab協議叢集部署來解決的。注意,DB和Redis的複製一般都是非同步的,也就是說某些時刻分散式鎖發生故障可能存在資料不一致問題,而Zookeeper本身透過zab協議保證叢集內(至少n/2+1個)節點資料一致性。
鎖喚醒: DB和Redis分散式鎖一般不支援喚醒機制(也可以透過應用層自己做輪詢檢測鎖是否空閒,空閒就喚醒內部加鎖執行緒),Zookeeper可透過本身的watcher/notify機制來做。
使用分散式鎖,安全性上和多執行緒(同一個程式內)加鎖是沒法比的,可能由於網路原因,分散式鎖服務(因為超時或者認為client掛了)將加鎖資源給刪除了,如果client端繼續操作共享資源,此時是有隱患的。
因此,對於分散式鎖,一個是要儘量提高分散式鎖服務的可用性,另一個就是要部署同一內網,儘量降低網路問題發生機率。
這樣來看,貌似分散式鎖服務不是“完美”的(PS:技術貌似也不好做到十全十美 :( ),那麼開發人員該如何選擇分散式鎖呢?最好是結合自己的業務實際場景,來選擇不同的分散式鎖實現,一般來說,基於Redis的分散式鎖服務應用較多。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31545684/viewspace-2284789/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一文弄懂“分散式鎖”分散式
- 「分散式」實現分散式鎖的正確姿勢?!分散式
- Redis分散式鎖的正確實現方式Redis分散式
- Redis 分散式鎖的正確開啟方式Redis分散式
- 掌握Redis分散式鎖的正確姿勢Redis分散式
- 分散式鎖實現的正確開啟方式分散式
- 一文弄懂分散式場景中各種鎖的原理及使用分散式
- 這才是實現分散式鎖的正確姿勢!分散式
- 基於Redis分散式鎖的正確開啟方式Redis分散式
- 分散式之抉擇分散式鎖分散式
- 【新夢想老師分享】分散式鎖的正確"姿勢"分散式
- 選擇catalyst是正確的麼?
- 選擇華瑞,是我做的最正確的選擇
- MongoDB是不是正確的選擇? - simplethreadMongoDBthread
- 【UX設計】如何為你的遊戲選擇正確的字型?UX遊戲
- redis應用系列一:分散式鎖正確實現姿勢Redis分散式
- 在svg和canvas中你該如何選擇?依據是什麼?SVGCanvas
- 分散式鎖的多種實現方式,你瞭解嗎?分散式
- 分散式鎖為什麼要選擇Zookeeper而不是Redis?分散式Redis
- 面試官:你真的瞭解Redis分散式鎖嗎?面試Redis分散式
- 一篇文章帶你解讀redis分散式鎖的發展史和正確實現方式Redis分散式
- 如何選擇正確的雲伺服器配置伺服器
- 如何選擇正確的Node框架:Next, Nuxt, Nest?框架UX
- 如何正確選擇適合的CRM系統?
- 值得收藏!選擇正確BI工具的最佳指南
- ES6 箭頭函式你正確使用了嗎函式
- .NETCore C# 中級篇2-4 一文帶你完全弄懂正規表示式NetCoreC#
- 大資料時代,如何根據業務選擇合適的分散式框架大資料分散式框架
- Redis 分散式鎖的正確實現原理演化歷程與 Redission 實戰總結Redis分散式
- 如何選擇正確的雲伺服器配置呢伺服器
- 選擇正確DevSecOps解決方案的七個技巧dev
- [分散式][分散式鎖]淺談分散式鎖分散式
- 一文徹底弄懂Spring IOC 依賴注入Spring依賴注入
- 這才是分散式事務的正確開啟方式!分散式
- 如何正確選擇Web前端培訓機構Web前端
- 如何選擇正確的Node框架:Express,Koa還是Hapi?框架ExpressAPI
- 如何為微服務選擇正確的訊息佇列微服務佇列
- 分散式事務的這些常見用法都有坑,來看看正確姿勢分散式