秒殺系統設計思路

什麼是秒殺系統?

秒殺的場景一般都是商家以促銷、預熱等活動的形式出現,其主要目的是全民營銷以及用戶的激活和拉新。

業務特點

  1. 高併發(讀、寫);
  2. 高可用;
  3. 數據最終一致性要求高(不能出現庫存超扣的情況);

設計原則

  1. 熱點隔離,秒殺的熱點數據隔離處理,報障整個系統的高可用;
  2. 讀數據,動靜分離,靜態數據緩存在客戶端或者CDN,動態數據緩存在服務端;
  3. 讀數據,不需要做強一致性校驗,最終一致性即可;
  4. 寫數據,分層校驗、“削峯填谷”、限流保護;
  5. 寫數據,強一致性校驗。

具體方案

熱點隔離

核心思想,不要讓1%的請求影響到另外的99%,隔離出來後也更方便對這1%的請求做針對性優化。設計多層次隔離:

  • 業務隔離。與產品運營提前溝通好活動產品和方案,做好預熱準備工作。技術上,把相關產品加入熱點隔離範圍。

  • 系統隔離。獨立部署集羣,和普通商品的服務區分處理。

  • 數據層隔離。數據庫、緩存的獨立部署。

動靜分離

核心思想,細化讀請求內容,針對性的進行多級緩存處理。

  • 靜態數據。像秒殺活動的商品、活動方案、頁面樣式等等數據,在整個活動週期內基本不會發生變化,都屬於靜態數據。靜態數據處理,在CDN和客戶端加上雙緩存。CDN緩存解決大量用戶的請求問題。客戶端緩存保證單個用戶的多次請求問題,並降低CDN節點壓力以及網絡傳輸耗時。
  • 動態數據。像商品庫存、參與人數等變化數據,都屬於變化數據。可以在web端緩存,保證最終一致性即可。
    • 緩存中間件。如redis、Tair等緩衝中間件。
    • 本地緩存。雖然緩存中間件可以處理10W+/S的讀請求,但是畢竟還是有請求極限,網卡限制等。在本機加上本地緩存,不用去請求緩存組件,減少網絡傳輸提高效率,也可以一定程度上保護緩存組件。

讀數據的最終一致性

像讀數據的請求,如秒殺庫存、參與人數等數據,從業務場景上並不需要絕對的實時數據,允許一定程度的數據延遲。所以可以加上緩存,來應對高併發場景,並定時更新數據即可。更新方式主要兩種:

  • 被動更新。設置緩存失效時間,當緩存失效後可以從DB加載數據到緩存中。可能出現緩存併發(也叫緩存擊穿)問題。
  • 主動更新。在失效前提前異步加載最新數據到緩存(應對緩存併發問題)。解決緩存併發問題,唯一不足的是可能會出現各個應用節點極其短暫的不一致情況,這個對互聯網應用來說是完全可以接收的。推薦採用。

寫數據,分層校驗

核心思想,儘早返回處理結果。從請求的全鏈路角度出發,分層校驗,減少無效請求,儘早返回結果。
請求漏斗模型

寫數據,“削峯填谷”

針對瞬時的大量請求的高併發問題,一般的通用解決思路就是加入隊列緩衝,實現“削峯填谷”。

寫數據,限流保護

沒有什麼是不可能的。就算我們做了上面一系列的優化措施,還是有可能扛不住請求的併發。爲了保證系統的可用性,還是需要做限流保護。限流後可能會出現雪崩效應(單機到達限流閥值拒絕請求,這部分請求轉嫁給其他機子,其他機子也會更容易到達限流閥值,最終導致服務不可用。)。

  • 解決方案:
    • 提前準備橫向擴展的服務器。數據提前預估。
    • 設置多級限流閥值。
    • 當到達一定閥值的時候,動態橫向擴展服務器,併發送警告通知。

寫數據,強一致性校驗

  • 數據庫層(不推薦)
    • 悲觀鎖。innodb行鎖(共享鎖/排他鎖)。
    • 樂觀鎖。version版本號控制。
  • 應用層
    推薦: 在應用層虛擬扣除庫存成功後,然後隊列處理到DB層。

    redis的watch命令+multi命令,扣除成功後同步到訂單子系統。

    watch命令對目標Key進行標記後,當事務提交時,如果監控到目標Key對應的值已經發生了改變,那麼也就則意味着版本號發生了改變,因此這一次的事務提交操作就失敗。

提高下單成功率

通過redis的watch命令處理,基本上可以滿足秒殺場景的寫需求。但是當秒殺的商品數量比較多的時候,比如上萬的庫存量,watch的重新嘗試次數會變多,整體上來講碰撞率會很高,從用戶角度來看就是秒殺時系統反應時間過長,體驗不好。

分析原因:根本上來說所有請求是對單個商品key進行處理導致的。如果採用分流方式,某部分請求key1,某部分請求key2,通過分流針對不同的key進行watch,降低碰撞概率,提高下單成功率。

把1個商品key拆分爲1個父key+多個子key,每個key佔用一部分庫存,所有子key對應的商品數量和等於父key對應的商品數量。

  • 隨機watch子key,當這個子key產生碰撞後,重新隨機watch子key。
  • 當子key的事務成功提交後,把父key的商品數量相應扣除。
  • 當子key的商品數量爲0時,維護子key列表剔除這個子key。
  • 當父key的商品數量爲0或者子key列表爲空時,直接返回庫存爲0的結果。

拆分的數量也不是越多越好,一般10-20個子key,理論上會有10-20倍成功率提升。

參考文檔

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