作者:不遠復
cnblogs.com/dreamworlds/p/5398468.html
1、并發佇列的選擇
Java的并發包提供了三個常用的并發佇列實作,分別是:ArrayBlockingQueue、ConcurrentLinkedQueue 和 LinkedBlockingQueue ,
ArrayBlockingQueue是初始容量固定的阻塞佇列,我們可以用來作為資料庫模塊成功競拍的佇列,比如有10個商品,那么我們就設定一個10大小的陣列佇列,
ConcurrentLinkedQueue使用的是CAS原語無鎖佇列實作,是一個異步佇列,入隊的速度很快,出隊進行了加鎖,性能稍慢,
LinkedBlockingQueue也是阻塞的佇列,入隊和出隊都用了加鎖,當隊空的時候執行緒會暫時阻塞,
在請求預處理階段,由于我們的系統入隊需求要遠大于出隊需求,一般不會出現隊空的情況,所以我們可以選擇ConcurrentLinkedQueue來作為我們的請求佇列實作,
2、請求介面的合理設計
一個秒殺或者搶購頁面,通常分為2個部分,一個是靜態的HTML等內容,另一個就是參與秒殺的Web后臺請求介面,
通常靜態HTML等內容,是通過CDN的部署,一般壓力不大,核心瓶頸實際上在后臺請求介面上,這個后端介面,必須能夠支持高并發請求,同時,非常重要的一點,必須盡可能“快”,在最短的時間里回傳用戶的請求結果,
為了實作盡可能快這一點,介面的后端存盤使用記憶體級別的操作會更好一點,仍然直接面向MySQL之類的存盤是不合適的,如果有這種復雜業務的需求,都建議采用異步寫入,

當然,也有一些秒殺和搶購采用“滯后反饋”,就是說秒殺當下不知道結果,一段時間后才可以從頁面中看到用戶是否秒殺成功,
但是,這種屬于“偷懶”行為,同時給用戶的體驗也不好,容易被用戶認為是“暗箱操作”,
推薦:秒殺系統設計的 5 個要點,
3、高并發下的資料安全
我們知道在多執行緒寫入同一個檔案的時候,會存現“執行緒安全”的問題(多個執行緒同時運行同一段代碼,如果每次運行結果和單執行緒運行的結果是一樣的,結果和預期相同,就是執行緒安全的),
如果是MySQL資料庫,可以使用它自帶的鎖機制很好的解決問題,但是,在大規模并發的場景中,是不推薦使用MySQL的,秒殺和搶購的場景中,還有另外一個問題,就是“超發”,如果在這方面控制不慎,會產生發送過多的情況,
我們也曾經聽說過,某些電商搞搶購活動,買家成功拍下后,商家卻不承認訂單有效,拒絕發貨,這里的問題,也許并不一定是商家奸詐,而是系統技術層面存在超發風險導致的,
超發的原因
假設某個搶購場景中,我們一共只有100個商品,在最后一刻,我們已經消耗了99個商品,僅剩最后一個,
這個時候,系統發來多個并發請求,這批請求讀取到的商品余量都是99個,然后都通過了這一個余量判斷,最終導致超發,(同文章前面說的場景)

在上面的這個圖中,就導致了并發用戶B也“搶購成功”,多讓一個人獲得了商品,這種場景,在高并發的情況下非常容易出現,
悲觀鎖思路
解決執行緒安全的思路很多,可以從“悲觀鎖”的方向開始討論,
悲觀鎖,也就是在修改資料的時候,采用鎖定狀態,排斥外部請求的修改,遇到加鎖的狀態,就必須等待,

雖然上述的方案的確解決了執行緒安全的問題,但是,別忘記,我們的場景是“高并發”,也就是說,會很多這樣的修改請求,每個請求都需要等待“鎖”,某些執行緒可能永遠都沒有機會搶到這個“鎖”,這種請求就會死在那里,
同時,這種請求會很多,瞬間增大系統的平均回應時間,結果是可用連接數被耗盡,系統陷入例外,推薦:并發控制--悲觀鎖和樂觀鎖詳解,
FIFO佇列思路
那好,那么我們稍微修改一下上面的場景,我們直接將請求放入佇列中的,采用FIFO(First Input First Output,先進先出),這樣的話,我們就不會導致某些請求永遠獲取不到鎖,看到這里,是不是有點強行將多執行緒變成單執行緒的感覺哈,

然后,我們現在解決了鎖的問題,全部請求采用“先進先出”的佇列方式來處理,那么新的問題來了,高并發的場景下,因為請求很多,很可能一瞬間將佇列記憶體“撐爆”,然后系統又陷入到了例外狀態,
或者設計一個極大的記憶體佇列,也是一種方案,但是,系統處理完一個佇列內請求的速度根本無法和瘋狂涌入佇列中的數目相比,
也就是說,佇列內的請求會越積累越多,最終Web系統平均回應時候還是會大幅下降,系統還是陷入例外,
樂觀鎖思路
這個時候,我們就可以討論一下“樂觀鎖”的思路了,樂觀鎖,是相對于“悲觀鎖”采用更為寬松的加鎖機制,大都是采用帶版本號(Version)更新,
實作就是,這個資料所有請求都有資格去修改,但會獲得一個該資料的版本號,只有版本號符合的才能更新成功,其他的回傳搶購失敗,推薦:并發控制--悲觀鎖和樂觀鎖詳解,
這樣的話,我們就不需要考慮佇列的問題,不過,它會增大CPU的計算開銷,但是,綜合來說,這是一個比較好的解決方案,

有很多軟體和服務都“樂觀鎖”功能的支持,例如Redis中的watch就是其中之一,通過這個實作,我們保證了資料的安全,
推薦去我的博客閱讀更多:
1.Java JVM、集合、多執行緒、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架構、阿里巴巴等大廠最新面試題
覺得不錯,別忘了點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/172509.html
標籤:Java
