萬人搶紅包高並發業務場景模擬常規解決方案


這可能是我在博客園的第一篇認真寫的文章,由於之前的公司工作太忙,一直沒有時間管理,平時登錄博客也只是把不常見問題的解決辦法記錄一下,現在離職了,時間較為富裕,在准備新面試之前將去年遇到的難點一一梳理一下。

 

高並發業務場景在電商系統中經常出現,尤其是庫存方面,搞不好就要超賣,給公司造成直接的經濟損失,雖然解釋權在公司,但這也對用戶的體驗不好,下面我會將去年遇到的高並發搶紅包解決方案與代碼寫下來。

 

假設我們模擬3萬會員均攤20萬元現金紅包,並假設流量點在同一時間,同時涌入,如果按照正常的業務邏輯的話,發生超賣的情況是必須的,如果從代碼層面限制,比如引入synchronizelock一類的鎖機制控制的話,首先在分布式系統中是不支持的,僅適合單機,單機又容易引發單點故障,性能等問題,所以此種做法不推薦。

 

1、悲觀鎖

 

這里我們使用數據庫內部機制提供的一種鎖的辦法,在多線程並發競爭期間,如果有一條線程占有了控制權,那么其他線程將無法獲取直到線程釋放控制權再次競爭,這種做法能完全解決超賣問題的發生,但隨之而來的是:

 

 

 

線程、鎖頻繁的掛起釋放會急劇消耗CPU資源,使得性能下降,在高並發環境中,會帶來非常恐怖的后果。

但這種做法不是不可以用,需要考慮實際業務以及流量,例如大額交易,通過風險控制系統引導,超過上千萬的單筆交易,通知人工監控是一個方案,引入悲觀鎖來加強安全也是可行的。

 

實現方法:

select xxx from xxx where xxx = xxx for update 

如果條件為主鍵索引,那么此次獲得的鎖將是一個行級鎖,如果是非主鍵字段,那么鎖機制可能會把整張表鎖定,這個結果是不一定的,例如mysql數據庫就會自己根據實際情況選擇哪一種更適合,有可能你是主鍵索引,應該是得到行鎖,但如果mysql認為表鎖更合適,你獲得的將是表鎖。具體業務具體分析。

 

2、樂觀鎖

 

樂觀鎖是一種非阻塞線程並發的機制,他的實現不會依賴數據庫內部機制,解決了之前悲觀鎖阻塞、線程頻繁掛起恢復問題,樂觀鎖使用的是CAS原理,在Java語言中concurrent包就是建立在CAS基礎上的。

 

什么是CAS?

官方:CAS,compare and swap的縮寫,中文翻譯成比較並交換。

概述:對多線程共享資源,先取得舊值保存,在提交時進行取現有值與原有值進行比較

 

什么是ABA?

講到CAS原理,就會引出另一個問題,那就是經典ABA問題,這個東西介紹起來篇幅太大,具體的請網上搜索資料,簡單來講就是由於多線程之間業務邏輯問題會導致取到的舊值發生改變,存在回退的可能性,解決辦法是在數據表中加入一個非關鍵的version字段,強制遞增,沒有回退操作,ABA問題就解決了。

 

 

實現方法:

update xxx set xxx = xxx, version = version + 1 where xxx = xxx and version = ?

 

經過測試,這種做法跟一開始不用任何鎖的性能是基本一致的,但是這種做法會大大的提高失敗率,如果業務要求不嚴格的話,到這基本就可以解決我們的問題,那么接下來該怎么解決失敗率的問題?

 

可重入鎖:

這僅僅是一種機制,就好比我騎單車在XXX路停下,那么下次我怎么才能再找到這輛單車?用最原始的方法,我在這個地方畫個圈圈什么的標記一下,那么可重入鎖也是這樣實現。

 

我們暫時不用可重鎖,目前他的最好實現是利用zookeeper的子節點來實現,我們只是單獨實現重入機制

 

 

在最外層加一層for循環來重試即可,此操作冪等的,所以數據不會錯亂,一般情況下只要上一步實現就可以了,為了保險可以加入重入機制

 

閱讀源代碼請訪問https://github.com/wade-zh/hc-red-package

其中又多加了利用redis lua腳本的原子性特點實現高性能的方案


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM