天天看點

商品秒殺系統設計思路

秒殺是電商網站的一種銷售方式,以特定時間段内較低的商品價格來吸引消費者購買,并對銷售庫存進行限制。這樣必然會造成特定時間段(一般幾秒到幾十秒不等)大量的使用者對某商品進行搶購,并會有庫存不足,搶購失敗的情況。

大量使用者在秒殺時間點發起購買請求,造成網站流量瞬間激增;

秒殺的商品一般庫存較少,隻有少數使用者能夠購買,要控制好庫存,防止超賣;

整個系統關鍵在于支撐短時間内的高并發,降低資料庫壓力,業務和普通商品購買差別不大。

限流,通過前端頁面減少并發通路,比如在搶購的時候需要輸入驗證碼,降低同一時間通路量;

減少資料庫讀寫,對商品庫存的控制,放到記憶體資料庫中,增加讀取效率;

削峰,短時間内大量的請求進入系統,并發通路伺服器和資料庫,肯定會拖垮整個系統,可以使用消息隊列的方式削減并發請求,把瞬間的高流量變成一段時間内的平穩流量。

方案一:mysql排他鎖

排他鎖,又稱寫鎖,顧名思義,就是排他鎖不能和其他鎖共存。在一個事務擷取了一個資料行的排他鎖,那麼其他事務就不能擷取該行的排他鎖,隻有擷取到該資料行的排他鎖的事務,才可以對該行進行讀取和修改。這樣我們在讀取商品庫存的時候,就可以使用排他鎖的方式,即在一個事務對庫存進行讀取的時候,另一個事務無法讀取庫存,相當于在資料行層面進行串行化,讓讀取商品庫存的請求順序執行,保證庫存讀到的準确性,也就是解決“賣超”問題。

排他鎖使用方式,mysql預設在innoDB引擎下update、insert、delete語句都會使用排他鎖,另一種常用的方式是SELECT...FOR TABLE UPDATE查詢添加排他鎖;

mysql排他鎖方式能夠應對并發量較小的商品搶購,因為每次搶購請求都會讀取資料庫,是以無法應對高并發。

方案二:redis存儲商品庫存

高并發下,性能瓶頸在于資料庫,因為java服務可以通過擴充機器的方式,部署多套分擔服務壓力,但是資料庫不好擴充。是以如果能夠解決資料庫瓶頸,秒殺業務的高并發壓力也就得以解決。

資料庫瓶頸的關鍵又在于每次秒殺請求都通路mysql資料庫擷取庫存,是以重點在于優化讀取庫存操作。

Redis作為高性能NoSQL資料庫,讀寫操作都在記憶體中進行,其官方說每秒能處理10萬左右的讀寫。是以可以使用redis來緩存商品庫存,秒殺請求都會先去Redis中查詢庫存是否足夠,如果足夠,再進行下一步訂單操作。

但是這種方案也有一定局限性,在并發量一定的情況下可以使用。Redis本身并不具備并發處理能力,在超高并發的秒殺業務中其實Redis也會出現性能瓶頸;另一方面,如果單純用redis擋住沒有庫存的請求,那麼對于庫存充足時透過redis的請求,還是會馬上打到mysql資料庫上,如果直接打在mysql的請求量大的話,資料庫還是會面臨并發壓力。

是以,單用redis還不能解決高并發業務。

方案三:redis和MQ消息隊列

消息隊列是解決高并發的有效工具,方案二中透過redis的大量請求,不直接請求資料庫下單,采用異步下單方式,先進入消息隊列,然後排隊消費,将瞬間并發通路變成一段時間正常通路資料庫。

對于秒殺請求,redis中庫存不足的請求直接傳回庫存不足;庫存足夠的請求,表示可以進行下單操作,那麼進入消息隊列,異步下單,同時秒殺請求傳回“排隊中”狀态,許多平台采用這種方式來應對并發。傳回“排隊中”狀态的秒殺請求,用戶端要想知道秒殺結果,就需要調用查詢秒殺結果接口,此時查詢有三種狀态“仍然排隊中”、“秒殺失敗”和“秒殺成功”。對于“仍然排隊中”的秒殺請求,用戶端需要輪詢調用秒殺結果查詢接口直到傳回結果狀态“失敗”或者“成功”,秒殺結果查詢接口去查詢redis中是否生成了對應訂單,查詢redis中庫存是否足夠,以此來判斷使用者是否秒殺成功。

商品秒殺業務的并發也不隻是秒殺接口,還有秒殺商品詳情頁的通路、秒殺結果查詢等。處理并發,要合理利用緩存機制,減少資料庫的壓力。

限流:圖形驗證碼方式,請求秒殺接口需要驗證圖形驗證碼正确性,正确識别驗證碼的時間不一緻,能夠緩解瞬間并發壓力;

防刷:一個使用者對一個路徑的通路次數在一定時間内有限制,使用redis可以解決;

接口位址隐藏:接口位址傳參,保證秒殺接口不是一個固定路徑,防止接口被刷,同時也可以有效隐藏秒殺位址。

繼續閱讀