來源:北大青鳥總部 2023年05月29日 10:36
當下在互聯網技術架構中,最流行的莫過于分布式架構了。為什么大家紛紛都采用分布式架構呢?
1、高效低廉,將部署在高性能機的程序分散在多個小型機中部署;
2、擴展性強,可隨著業(yè)務的擴展而橫向擴展系統的性能;
3、可靠性強,當系統中一臺或幾臺出現故障時,仍然有其它機器在提供服務;
4、并發(fā)性強,各臺機器同時運作提供服務。
分布式,真香!
不過使用分布式架構也會存在一些問題,最嚴重的問題便是數據一致性問題。因為業(yè)務是部署在多臺機器上,由于時間空間的不一致,從而導致數據會不一樣,分布式的CAP理論已經告訴我們“分布式系統無法同時滿足一致性Consistency、可用性Availability、分區(qū)容錯性Partitiontolerance,最多滿足兩項”。對于數據不一致的問題,互聯網有幾種思考,比如BASE服務基本可用,犧牲暫時的數據不一致,只要數據最終一致即可;采用分布式事務進行解決;采用分布式鎖進行解決。而今天我們要介紹的便是分布式鎖的解決方案。
首先來看一個具體的case解釋為什么需要分布式鎖。在電商業(yè)務采用分布式架構后,程序部署在3個tomcat容器中(1個tomcat容器代表一個服務器,3個tomcat可理解在北京上海深圳都有部署電商服務),成員變量A代表商品數量。在北京的Alice,上海的Bob,深圳的Tom,都分別發(fā)起了購買或取消iPhone12的用戶請求,經過Nginx負載均衡將Alice的請求發(fā)給了北京服務器,Bob的請求發(fā)給了上海服務器,Tom的請求發(fā)給了深圳服務器,這時候每臺服務器都會對iPhone12這個商品數量進行更改,Alice的請求是將商品數量加到200,Bob的請求是將商品數量減少100,Tom的請求是將商品數量加1,如果對于商品數量的修改沒有任何限制,整體就會亂起來,可能Bob的先減少,Tom的在增加,數據就完全亂了,所以需要分布式鎖解決方案。
鎖的概念并不是在分布式中才存在,傳統互聯網的開發(fā)中也存在鎖。比如在多進程處理請求時,內存資源就會不足,這時候操作系統會使用信號量來解決資源的搶奪,如果信號量的值大于0,則將信號量數值減1,同時分配內存資源,如果信號量的值小于0,則進程處于等待狀態(tài),有其他進程操作執(zhí)行完畢后,信號量數值加1,喚醒等待的進程。
總結一下,實現鎖有三個要素:
1、有存儲鎖的空間,在多進程中,內存就是存儲鎖的空間,通過對鎖的控制實現不同進程的訪問控制。2、能唯一標識,不同的空間用不同的鎖保護,那必須要唯一標識。
3、有狀態(tài),即存在、不存在。在分布式系統環(huán)境中,分布式鎖就是一個變量一個方法在同一時間只能被一個機器的一個線程執(zhí)行,對分布式鎖的實現也提出了更高的要求,即需要高性能高可用的獲取與釋放鎖,需要鎖超時機制,避免死鎖出現。
那么如何實現分布式鎖呢?業(yè)內有三種實現方式:
1、基于數據庫;
2、基于redis;
3、基于Zookeeper。
對于第一種實現方案,很簡單,我們知道在傳統數據庫中是有ACID事務原子性、一致性、持久性、可用性規(guī)則的,如果基于數據庫實現分布式鎖,只需要在數據庫中創(chuàng)建一個表,表中包含方法名,對方法名加上唯一索引,想要執(zhí)行該方法時,就使用這個方法名向表中插入數據,插入時,其它數據都沒法插入,等于獲得鎖,成功插入后,刪除對應的數據釋放鎖。這種方案的好處就是簡單,但存在的問題是對數據庫要求高,因為數據庫的可用性、性能會直接影響分布式鎖的可用性,數據庫可能需要主從部署、讀寫分離。
對于第二種實現方案,只需要使用redis的命令setnx、expire、delete就可以了(請允許我再感嘆一下,redis真的太好用了,又簡單性能又好),setnxkeyvalue就會給某個變量賦予一個值,返回1,當業(yè)務請求來時,如返回key值為1,線程獲得鎖,如果key值為0,線程搶鎖失敗。
對于第三種實現方案,我們知道zookeeper是一個分布式協調服務,它內部是一個分層的文件系統目錄樹結構,同一個目錄下只能有一個唯一文件名,因此當實現分布式鎖時,只需要創(chuàng)建一個目錄,線程想要獲取鎖就在目錄下創(chuàng)建臨時順序節(jié)點,然后遍歷獲取是否存在比自己小的節(jié)點,如果存在則獲取鎖失敗,如果不存在則獲取鎖成功,缺點就是會頻繁的創(chuàng)建節(jié)點。
通過本文的介紹,認真閱讀的小伙伴又獲得了分布式架構使用的一個技巧。在分布式環(huán)境中,對資源的上鎖非常重要,通過分布式鎖解決了數據的一致性問題,小伙伴們可以根據自己業(yè)務實際情況選擇合適的分布式鎖方案噢~