秒殺商品設計
前端限制
前端控制,不能重複點擊
精簡sql
典型的一個場景是在進行扣減庫存的時候,傳統的做法是先查詢庫存,再去update。 這樣的話需要兩個sql,而實際上一個sql我們就可以完成的。
可以用這樣的做法: update miaosha_goods set stock =stock-1 where goos_id ={#goods_id}
and version = #{version} and sock>0 ;
這樣的話,就可以保證庫存不會超賣並且一次更新庫存,還有注意一點這裏使用了版本號的樂觀鎖,相比較悲觀鎖,它的性能較好。
同一個用戶xx秒內重複請求直接拒絕
具體多少秒需要根據實際業務和秒殺的人數而定,一般限定爲10秒。
具體的做法就是通過redis的鍵過期策略,首先對每個請求都從 String value = redis.get(userId) ;
如果獲取到這個 value爲空或者爲null,表示它是有效的請求,然後放行這個請求。如果不爲空表示它是重複性請求,直接丟掉這個請求。
如果有效,採用 redis.setexpire(userId,value,10).value
可以是任意值,一般放業務屬性比較好,這個是設置以userId爲key,10秒的過期時間(10秒後,key對應的值自動爲null)
使用redis限流
限流的實現可以直接使用 Guava
的 RateLimit
方法,分佈式的話,可以緩存在redis
中
使用redis加載商品,數據庫樂觀鎖更新
- 將商品數據加載到
redis
- 用戶進來查看
redis
中的庫存數量num
,庫存不足直接拋出 - 進入數據庫,更新數據庫庫存(需要注意的是,這裏要進行樂觀鎖判斷,並且還要校驗庫存數量
num
,可以正好一起做樂觀鎖判斷),不匹配也直接拋出 - 更新
db
後,更新redis
緩存
這裏有一個樂觀鎖判斷,如果一個線程
A
在走上面的流程,線程B
進入,查看到庫存,在進入步驟3進行更新數據時,因爲緩存中庫存與數據庫中的庫存不一致,導致更新失敗。
異步下單
爲了提升下單的效率,並且防止下單服務的失敗。 需要將下單這一操作進行異步處理。
最常採用的辦法是使用隊列,隊列最顯著的三個優點: 異步、削峯、解耦 。
這裏可以採用rabbitmq,在後臺經過了限流、庫存校驗之後,流入到這一步驟的就是有效請求。 然後發送到隊列裏,隊列接受消息,異步下單。
下完單,入庫沒有問題可以用短信通知用戶秒殺成功。 假如失敗的話,可以採用補償機制,重試。
服務降級
假如在秒殺過程中出現了某個服務器宕機,或者服務不可用,應該做好後備工作。 之前的博客裏有介紹通過Hystrix進行服務熔斷和降級,可以開發一個備用服務。
假如服務器真的宕機了,直接給用戶一個友好的提示返回,而不是直接卡死,服務器錯誤等生硬的反饋。