JAVA 高併發下單解決方案-分佈式鎖

背景:高併發情況下,商品出現超賣的情況。

最終目標:保證數據的最終一致性。

Contrrler 層框架 : Spring MVC

第一次嘗試: 最初的時候,發現Spring MVC是一個單例多線程的Controller框架。它在多線程同時訪問的時候會出現線程不安全的情況。經過分析,發現如果不建立 成員變量 的話,線程不安全的情況是不會出現的。如果需要建立成員變量,解決這個問題可以通過 ThreadLocal 來解決這個問題。 ThreadLocal 可以存儲 獨屬於 線程的變量。(PS:說了這麼多還是沒解決這個問題)

 

第二次嘗試:發現不是Spring MVC的問題後,開始使用 Java1.5新特性中的Lock鎖來解決這個問題。保證同一時期,只有一個線程可以進行寫的操作。(暫時解決了問題)

第二次嘗試失敗原因:後續訂單量又一步的增長,發現又出現了超賣的情況。細查之後,發現是因爲有多個 tomcat 容器,多個tomcat之間的鎖不同步導致。

 

第三次嘗試分佈式同步鎖。在經過大量的資料查詢後,分佈式鎖無法滿足 一致性(Consistency)、可用性(Availability)和分區容錯性,最多隻能滿足其中兩項。所以,建議各位在程序設計之初就要做出取捨。在互聯網這個行業中,我所接觸的項目中大多都犧牲了 系統的強一致性 ,從而來換取 系統的可用性(但是系統一定要確保 數據的最終一致性)。爲了確保 數據的最終一致性,分佈式同步鎖就這樣誕生了。

目前有一下幾種方案:

1,基於緩存實現分佈式鎖

基於緩存實現的分佈式鎖,在性能上是非要好的,並且集羣部署

使用 setNX 來存儲 鎖的超時時間戳

語法:

SETNX key value

setNX 解析: 

  將 key 的值設爲 value ,當且僅當 key 不存在。

  若給定的 key 已經存在,則 SETNX 不做任何動作。

  SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫

返回值:

  設置成功,返回 1 。

  設置失敗,返回 0 。

問題一-死鎖:如果一個持有鎖的客戶端失敗或崩潰了不能釋放鎖

問題一-解決方案:可以根據 鎖的超時時間戳 來判斷是否發生了,如果當前時間大於 lock的值,說明該鎖已經失效,可以重新使用。但是要注意,發生這種情況不能 只del 然後 setNX。因爲這樣的話,當多個線程檢測到超時後,就會去 del 該鎖,然後持有他。

問題二-多線程持有鎖-解決方案:getSET

語法:

GETSET key value

將指定的key設置指定的value值,並且返回之前存儲的value值。當沒有返回值時,返回 null

假設現在 A1 服務器已經發生了死鎖問題。這時 A2 服務器 setNX操作返回0, A2服務器 get操作檢查是否超時。如果沒超時就繼續等待重試。

如果已超時,就通過 getSET 重新設置超時時間,如果 A2服務器拿到的是未超時的值。說明在此之前 A3服務器 先一步進行了 getSET 操作,那麼 A2服務器繼續等待重試。

注意:當持有鎖的 A1 服務器準備 del 的時候,一定要再次檢查一下 鎖是否超時。如果已超時就不必要解鎖了。

 

2,基於Zookeeper實現分佈式鎖

基於zookeeper臨時有序節點可以實現的分佈式鎖。

注意:基於 Zookeeper 實現的 分佈式鎖 有效的解決了 死鎖這個問題。但是在性能上是不如 緩存鎖的,而且需要對Zookeeper的原理有所瞭解。其次,使用Zookeeper也有可能出現問題。由於網絡抖動,客戶端與Zookeeper的連接斷了,這時Zookeeper就會以爲客戶端掛了,就會刪除鎖節點。這時就會產生併發問題。

 

3,基於 數據庫實現分佈式鎖

要實現分佈式鎖,最簡單的方式可能就是直接創建一張鎖表,然後通過操作該表中的數據來實現了。

當我們要鎖住某個方法或資源時,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。

 

總結:以上幾種方案其實都無法做到完美。

從 實現複雜度來說:Zookeeper與Redis差不多、最爲複雜的是數據庫

從 性能角度來說:Redis最好、其次Zookeeper、最差爲數據庫

從 可靠性來說:Zookeeper最好、其次Redis、最差爲數據庫

個人建議,在使用分佈式鎖中 不要去使用 數據庫的方式來進行操作。操作數據庫需要一定的開銷,並且行級鎖不一定靠譜。

關於Zookeeper實現的分佈式鎖,由於本人不是很瞭解Zookeeper,所以介紹的不是很詳細。如果有興趣的同學可以自己研究一下。(我會告訴你們,我用的就是 redis分佈式鎖嗎 ^_^)

PS:轉載請註明出處

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章