分散式秒殺系統-REDIS(高併發、高效能、庫存資料一致、不限語言-設計思路一致)
一、秒殺系統準備
1、首先需要能夠抗住基本請求流量的伺服器環境
2、高可用的redis環境(叢集、主從、資料持久化) ps:如果你的每秒請求只有幾百幾千一個REDIS完全夠用不需要額外操心,另外秒殺功能產品往往會加一個小梗,那就是開始秒殺時使用者需要填寫兌換的賬號才能發起秒殺,這裡根據使用者的操作時間本質上又做了一次/n的分流。其次前端本身還會驗證使用者是否有資格參與秒殺(餘額預驗證)。所以也就是說一場活動可能有10萬人參與,但是有資格的可能只有1萬人,再加操作時間線控制,真實併發最多幾千。一臺NGINX,一臺REDIS完全夠了。
PS:經過測試多節點高併發壓力測試
除能夠搶到的使用者(會真實落庫),其他被拒絕的使用者:tomcat介面請求響應速度和正常的一個無任何邏輯的介面響應時間誤差1毫秒左右,比如你一個API沒有任何處理:邏輯框架的響應時間為7~8毫秒。那麼秒殺介面響應大約在7~9毫秒
redis CPU負載基本忽略不計
所以最終的負載消耗,請以自己的伺服器架構配置為準即可。也就是說 本方案僅需要去關注伺服器環境的壓力支撐,不需要刻意關注系統。(redis如果達到瓶頸,首先很難很難,理論上幾十萬人都難以突破,那麼可以一個商品ID走一個reids即可)
3、寫程式碼的碼農
二、程式碼結構
1、redisKey準備【時間複雜度均為O(1)】
(1:秒殺使用者操作鎖 (參考:http://redisdoc.com/string/set.html 末尾)
命令:SET resource-name anystring NX EX max-lock-time
解鎖:if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
key: FLASH_LOCK
內容:{userId}
(2:秒殺庫存(參考:http://redisdoc.com/string/incr.html)
命令:incr \ INCRBY
key: FLASH_COUNT
內容:{日期_場次_商品ID}
2、API-秒殺資料查詢介面
獲取配置:場次、開始時間點、結束時間點、商品list{id,name,資格(如積分),總庫存} ,
注意:如果沒有配置中心或者動態配置需要放在資料庫的話,禁止每次走DB查詢,善用快取
配置計算:計算當前場次,返回商品列表,返回商品剩餘庫存(總庫存-FLASH_COUNT )
額外優化:
(1、本介面可加入應用記憶體快取模式,場次開始前1分鐘走記憶體,即將開始才走實時REDIS查詢剩餘庫存
(2、也可以走預生成配置模式,將計算好的邏輯放到REDIS中,介面僅僅做實時庫存查詢
3、API-秒殺介面
(1、使用者授權、基本引數驗證(一般在框架中)
(2、本場本商品ID是否已經沒有了庫存(單商品應用記憶體鎖定,立即減少redis get壓力)【沒有庫存立即響應結果】
(3、獲取使用者操作鎖FLASH_LOCK 【失敗立即響應,操作進行中】
getLock -》try {程式碼}catch (XX) {日誌}finally {解鎖}
(4、場次開始、商品有效等基本判定【失敗立即響應】
(5、立即redis get FLASH_COUNT 驗證商品是否有庫存【沒有庫存立即響應結果,寫入單商品應用記憶體鎖定】
(6、驗證使用者資格(如積分是否充足)【沒有餘額立即響應結果】
(7、開始秒殺 incr FLASH_COUNT 【驗證獲取到的序列是否超過庫存,步驟5微秒級同時get到會進入7被最終攔截,如果超過則響應沒有庫存了,寫入單商品應用記憶體鎖定】
(8、最終的幸運兒下單、落庫、走各自的獎勵發放流程(可同步可非同步)