Java 秒殺系統方案優化

Java 秒殺系統方案優化

這幾天一直在看這方面的視頻教程,今天總算是結束了,秒殺大體流程是很清楚的,但是一落到細節還是挺懵的,總結一下學到的知識點吧,寫的不好,還請見諒哈 ⊙ω⊙

一. 表結構的設計:

訂單表
商品表
秒殺訂單表
秒殺商品表

1. 爲什麼要這麼設計?

原因:假如沒有下面這兩張秒殺表的存在(自然,就需要向上面兩張表中添加關於秒殺的字段),這次我們僅僅是做了一個秒殺的業務,如果以後我們需要擴展優惠、促銷等活動,難道還要去修改訂單表的結構嗎?自然不可取。這是一方面的原因,另外單獨拎出來這2張表對併發也有一定的限制。

2. 數據庫主鍵的選擇?

數據庫主鍵如何選擇,mysql自增?UUID?NO,NO。
mysql自增主鍵就不要想了,平時都很少用的
UUID呢?互聯網中也沒有這樣用的,很low

真正推薦的是: twitter的雪花算法(snowflake)
鏈接: Twitter的分佈式自增ID算法snowflake

二. 頁面級高併發的優化

(前後端分離的,比前兩種方法好)
1.頁面緩存

過去我們是直接通過數據庫查詢數據,查詢後通過model放到域中,返回給前端,前端進行展示。
現在我們多幾步操作,我們在mysql查詢之後,通過SpringBoot的themleafViewResovler接口的實現類SpringWebContext來操作,手動進行渲染:thymeleafViewResolver。
鏈接:Java高併發優化之頁面緩存
然後再將這個HTML放入到redis中,再有別的訪問時候,直接去redis中取即可。

2.URL緩存

思路和上面大致差不多,這次是增加一個商品id作爲redis的key,利用上面的類手動渲染成html頁面,再存儲到redis中
鏈接:Java高併發優化之URL緩存

3. 頁面靜態化

仔細想一想,html可以被瀏覽器緩存,都被瀏覽器緩存了,怎麼能不快

4. 對象緩存

我們將用戶的信息放入到redis中。
弊端:每次修改用戶信息的時候還要更新緩存

5.其他方式:CDN優化+靜態資源的壓縮

三. 業務場景:超買、超賣

1.業務場景一:剩一個庫存的時候,兩個人同時調用減庫存方法容易將庫存變成負數。

想一想,當我們判斷剩一個庫存的時候,2個線程業務邏輯都到了減庫存的操作,這時候減的話,豈不是要變成負數了?
修改前的sql:
@Update(“update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId}”)
修改後的sql:
@Update(“update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0”)


多了些什麼?一個判斷,庫存數量大於0。因爲mysql有鎖的機制,所以線程是串行的,當一個進程將庫存修改爲0的時候,另一個線程進來的時候,發現stock_count>0了,這時候將不再進行修改,解決了庫存變負數問題

2. 業務場景二:一個用戶發送了兩個秒殺請求,這個請求是同步的,很巧妙的避開了判斷秒殺是否成功這個業務。所以最後的結果是生成2條訂單、2條秒殺訂單

業務時這樣的:秒殺成功後,我們向訂單表和秒殺訂單表寫入兩條數據
解決辦法:我們再秒殺訂單表中,將userId和goodsId創建 唯一索引

但凡有兩條一樣的數據,整體的業務就會回滾,保證了一個人一條秒殺訂單

四. 接口優化

思路:減少數據庫的訪問
1) 系統初始化,把商品庫存數量加載到Redis
2) 收到請求,Redis預減庫存,庫存不足,直接返回,否則進入3)
3) 請求入隊,立即返回排隊中
4) 請求出隊,生成訂單,減少庫存
5) 客戶端淪陷,是否秒殺成功

1. 系統初始化:

系統初始化的時候,直接將商品id和庫存數量放入到redis中
//我們的類實現InitializingBean接口,重寫afterPropertiesSet方法,將要秒殺的商品庫存數量放入redis中

2.redis預減庫存

當秒殺請求過來的時候,不走mysql,直接去redis中判斷庫存的數量,通過redis.decr來實現秒殺成功減少庫存

3.通過redis判斷重複秒殺

很簡單,秒殺成功的時候,我們將userId和商品id組合一個新的redis的key,放入到redis中。在進行重複判斷的時候,只需在redis中去判斷即可,又減少了連接數據庫的操作

4. MQ異步、解耦

下來的操作是,我們生成一個消息對象,將用戶和商品id放入到消息對象中,通過mq發送出去。我們只需要直接給用戶反饋排隊中,不需要我們在當前這個業務中寫減庫存,建訂單的操作。而是交給MQ的監聽者去做。

5.MQ監聽者執行業務邏輯

MQ的監聽隊列進行減庫存,創建訂單的操作。
通過MQ是不是解耦了呢

6. 進一步優化:nginx(橫向擴展)

在這裏插入圖片描述

五.安全優化

1. 秒殺接口地址隱藏

想一想,如果惡意用戶提前知道了我們的秒殺接口地址,他是不是就搞破壞呢?
解決:用戶在獲取秒殺地址的時候,我們在其中增加一個隨機參數,將這個隨機參數+用戶id作爲key存儲到reis中,在用戶進行秒殺的時候,前端將用戶這個參數傳遞過來,我們在去redis中進行驗證。

2. 數學公式驗證碼

這個就是驗證碼了,就不多說了

3. 接口防刷(對接口做限流)

根本思想就是:在一定時間內,每個用戶的訪問次數不得超過多少次,超過一定次數後,將不允許進行秒殺
具體的實現是 自定義註解+攔截器實現的
鏈接:高併發處理之接口限流

最後這個我畫的很low:
一個小小low圖

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