1. 程式人生 > >分布式鎖的實現概述

分布式鎖的實現概述

服務器 滿足 鎖定 stl nbsp lock 節點數據 數據一致性 宕機

做慣了講究響應速度的微小化web服務,當有人給我講分布式鎖時深刻懷疑說這個名詞的哥們要麽準備給我挖坑,要麽自己把架構玩脫了已經掉進了坑裏。這個東西雖然常見,但是稍有不慎就會掉坑裏出不來。

系統做的越多現在越來越害怕那種千鈞一發的系統,動輒每秒單例服務響應web業務請求百萬上下,這樣的實現功力確實佩服,就是不知道服務宕機時的瞬時數據在恢復時能不能達到同樣的速度了。從概率上來講,服務器越多,發生問題的概率越小,再加上自己常人天資,最希望的就是一臺服務器只處理一個請求-------當然這種開發富貴病也得治,不過那種炫技式的服務設計架構,除非是窮人家需要榨幹服務器所有的算力,否則還是花點錢,多買幾臺服務器均攤開的好。可以照著秒發百萬設計,但是最好貼著每秒負載上千的去玩。

扯遠了。使用分布式鎖就為了保證服務間數據一致性,那麽這個鎖怎麽進行分布式?

把鎖設置在各個服務點上就扯淡了,如果不講究響應時間並且不擔心系統復雜度,可以這麽玩。首先在收到服務請求時發給所有同服務點發出廣播,要求占用資源點,要求其他服務點暫停對這個資源點的請求。如果資源數據占用粒度比較小,那麽還好,其他服務點可以采用隊列堆積對著被占用的資源數據其中鎖定的那一部分的請求,比如某個指定賬戶的積分。如果沒有粒度區分,那就悲劇了,需要把所有資源的請求都堆積起來。請求完成,再發一次通知,並且還需要從堆積的請求裏按照一定規則挑出新的請求服務點以避免死鎖。如果不幸占用資源的服務掛掉了,還要再加一層根據時間條件的自旋鎖機制,估計這樣實現的話工程師都要哭了。所以,就別扯淡了。

把鎖設置在數據資源服務本身上。比如直接利用關系型數據庫的原子性事務或者redis自身的鎖機制,這種是常見的利用方式,關系型數據庫的響應速度可能會慢點,redis的響應速度相對好些。不過這種鎖,尤其是關系型數據庫有鎖粒度過大的問題,通常是表級別鎖,對並發性影響大,不適用於那種分分鐘百萬上下的。redis提供了一個分布式鎖的算法https://redis.io/topics/distlock。

請求隊列。將所有請求都放置到 一個隊列中先進先出,串行。這個方法雖然省事,但是不適用高響應系統大數據量系統,不然就是千鈞一發,系統一旦宕機,恢復數據時估計想打人。當然,為了提高響應速度可以依據一定規則對資源數據進行劃分,將請求隊列切分為多個,但是需要保證隊列間不能有請求資源交叉。

2和3的方式相結合,資源切分,請求歸隊,隊列再次切分,利用redis作為資源存儲,啟用redis的鎖算法,並且redis按照服務器進行數據存儲劃分而不是在一臺服務器進行分片。2的架構在請求數未突破redis的瓶頸時還好,一旦突破則性能會陡峭下降。所以此方案單次響應時間相比較2會長一些,但是支持的吞吐量不會完全受限於redis的性能瓶頸並且可以通過隊列服務增加來犧牲部分響應速度來降低性能下降速度,為redis劃分負荷增加處理時間。當然,架構的層級劃分也挺嚇人,價格也挺好看的................

1到4都是可切分的,但是有些數據是不太容易切分的。容許請求失敗的比如秒殺類的業務可以做切分,在子資源點數據不足時直接拒絕請求就可以了。但是面向成千上萬用戶發送積分並且積分有所預算的就不成了。當然,這個也可以切分,但是有後遺癥,如果被請求的子節點數據不足,那麽這個請求是不能認為失敗的,需要將請求路由到資源充足的節點上,那麽就需要在隊列請求分發前進行一次預占用處理,保證請求真正發出時目標資源點的數據是能夠滿足的,如果不能還需要進行查找再進行路由。還有一種比較坑的情況,就是各個子節點的資源都不足,但是總量是滿足,那麽還需要繼續再添加一層數據占用處理保障,對於突破子節點資源量的請求進行一套單獨的處理,並且還需要選擇預占用的節點進行預占用處理。

總結:

伴隨著一種種情況,似乎CAP原則大顯神威,但是情況沒有絕對,從企業的角度出發,好的用戶體驗是一方面,但是數據的正確性則更加重要一些。可以通過對業務進行細致分析嘗試將單次請求的資源需要量進行一些限制,反常則是妖,大多數的時候用戶的需求還是常見,能夠被系統一種較小的代價去實現出來。特定用戶高頻並且對資源大量請求的情況大多數來自與爬蟲或者機器人,人類的信息單位處理能力還是有限。當然,一些特定領域的信息處理則要另當別論。

本文轉載自:https://blog.csdn.net/xqj198404/article/details/81124177

分布式鎖的實現概述