如何設計一個秒殺系統總結

通過學習"如何設計一個秒殺系統"課程,總結秒殺相關的一些重要問題。

1.秒殺系統主要解決兩個問題,一個是併發讀,一個是併發寫。

      併發讀的優化就是儘量減少客戶端到服務端來讀數據,或者讀更少的數據;併發寫處理原則也一樣,在數據庫獨立出一個庫,做特殊處理。

 

2.秒殺的整體架構包括穩、準、快三個關鍵字:

     穩:即流量符合預期時整體架構要滿足高可用,就算超出預期也不能掉鏈子。

     準:要保證數據的一致性,不能超賣。

     快:系統性能要足夠高,儘可能做好系統優化。

 

3.秒殺系統的架構原則 "4要1不要"

      1)數據要儘量少:用戶請求的數據能少就少,包括上傳給系統的數據和返回給客戶端的數據。首先因爲數據傳輸需要時間,不管是請求數據還是返回數據都需要服務端做處理,非常消耗CPU;其次系統依賴的數據能少就少。

      2)請求數要儘量少:用戶請求的頁面返回後,瀏覽器渲染這個頁面還需要其他的額外請求,所以減少請求數可以減少資源消耗。例如常用的合併CSS和js文件。

      3)路徑要儘量短:就是用戶發出請求到返回數據,這個過程中經過的中間節點數。因爲每經過一個節點都會產生一個新的連接,同時增加了新的不確定性。所以縮短請求路徑不僅可以增加可用性,同時還能提高性能,減少延時。例如把多個相互強依賴的應用合併部署到一起,把遠程調用過程變成JVM內部調用。

      4)依賴要儘量少:指完成一次用戶請求依賴的系統或服務要儘可能少。減少依賴可以對系統進行分級,防止重要的系統被不重要的系統拖垮。

      5)不要有單點

淘寶早期秒殺架構案例:

     版本一:快速搭建一個簡單的秒殺系統,只需要把你的商品購買頁面增加一個“定時上架”功能,僅在秒殺開始時才讓用戶看到購買按鈕,當商品的庫存賣完了也就結束了。

     版本二:隨着請求量的加大(比如從 1w/s 到了 10w/s 的量級),版本一的架構很快就遇到了瓶頸,因此需要做架構改造來提升系統性能。這些架構改造包括:

     1)把秒殺系統獨立出來打造一個單獨的系統,這樣可以有針對性的優化,例如減少淘寶店鋪裝修的功能,減少頁面複雜度。

     2)系統獨立部署一個集羣,這樣秒殺的大流量不會影響到正常的商品購買集羣的機器負載。

     3)將熱點數據單獨放到一個緩存系統中,以提高讀性能。

     4)增加秒殺答題,防止有秒殺器。

     此時架構如下:

 

   版本三:這個架構仍然支持不了超過 100w/s 的請求量,所以爲了進一步提升秒殺系統的性能,我們又對架構做進一步升級。

       1)對頁面進行徹底的動靜分離,使得用戶秒殺時,不需要刷新整個頁面,只需要點擊搶寶按鈕,藉此把頁面刷新的數據降到最少。

       2)在服務端對秒殺商品進行本地緩存,不需要再調用依賴系統的後臺服務獲取數據,甚至不需要去公共的緩存集羣中查詢數據,這樣不僅可以減少系統調用,而且能夠避免壓垮公共緩存集羣。

       3)增加系統限流保護,防止最壞情況發生。

     經過這些優化,系統架構變成了下圖中的樣子。在這裏,我們對頁面進行了進一步的靜態化,秒殺過程中不需要刷新整個頁面,而只需要向服務端請求很少的動態數據。而且,最關鍵的詳情和交易系統都增加了本地緩存,來提前緩存秒殺商品的信息,熱點數據庫也做了獨立部署等。

4.如何做好動靜分離

   1)什麼是動靜分離?

      動靜分離:其實就是把請求的數據分爲動態數據和靜態數據。動態數據和靜態數據的主要區別是看頁面中輸入的數據是否和URL、瀏覽者、時間、地域相關,以及是否含有 Cookie 等私密數據。

   2)怎麼對靜態數據做緩存

       第一,應該把靜態數據緩存到離用戶最近的地方。常見的有:緩存在用戶瀏覽器上、CDN、服務端的Cache中。

       第二,靜態化改造就是要直接緩存HTTP連接。如下圖所示,Web 代理服務器根據請求 URL,直接取出對應的 HTTP 響應頭和響應體然後直接返回,這個響應過程簡單得連 HTTP 協議都不用重新組裝,甚至連 HTTP 請求頭也不需要解析。

      第三,讓誰來緩存靜態數據。不同語言寫的 Cache 軟件處理緩存數據的效率也各不相同。

  3)如何做動靜分離的改造

    以典型的商品詳情爲例介紹,看看這個頁面裏都有哪些動靜數據。我們從以下 5 個方面來分離出動態內容。

    1.URL唯一化,商品詳情繫統天然地就可以做到 URL 唯一化,比如每個商品都由 ID 來標識。

    2.分離瀏覽者相關的因素。瀏覽者相關的因素包括是否已登錄,以及登錄身份等,這些因素可以單獨拆分出來,通過動態請求獲取。

    3.分離時間因素,服務端輸出的時間通過動態請求獲取。

    4.異步化地域因素,頁面上與地域有關的因素通過異步方式獲取。

    5.去掉cookie,這裏說的去掉cookie並不是用戶端收到的頁面不含cookie了,而是緩存的靜態數據中不含cookie。

4)動態內容處理方式:ESI(Edge Side Includes)方案和CSI(Client Side Include)方案

    ESI(Edge Side Includes)方案:在WEB代理服務器上做動態內容請求,並將請求插入到靜態頁面中,當用戶拿到頁面時已經是一個完整的頁面了。

    CSI(Client Side Include)方案:單獨發起一個異步JS請求,向服務端獲取內容。

 5)動靜分離的幾種架構

     1.實體機單機部署

       將虛擬機改爲實體機部署,以增大cache的容量,並採用一致性hash分組的方式來提升命中率。

       實體機單機部署的優點:

        a.沒用網路瓶頸,而且能使用大內存。

        b.能夠提升命中率,又能減少Gzip壓縮。

        c.採用定時失效方式,減少cache失效壓力,

    2.統一cache層

            統一cache層就是將單機的cache統一分離出來,形成一個單獨的cache集羣。將cache層拿出來統一管理可以減少運維成         本,同時方便接入其他靜態化系統。

    優點:

        a.單獨一個cache層,可以減少多個應用使用cache的成本。

        b.統一cache層更易於維護。

        c.可以共享內存,最大化利用內存,不同系統之間的內存可以動態切換,從而能夠有效應用各種攻擊。

    3.上CDN

       將cache移動CDN上,有幾個問題需要解決:

       第一,失效問題

       第二,命中率問題,cache最重要的一個衡量指標就是高命中率,不然cache的存在就市區了意義。

       第三,發佈更新問題。

 

5.有針對性的處理好熱點數據     

  1)什麼是熱點?

      熱點分爲熱點操作和熱點數據。

      熱點操作:如大量刷新頁面、大量添加購物車等操作,這些操作可以抽象爲讀操作和寫操作。

      熱點數據:分爲靜態熱點數據和動態熱點數據。靜態熱點數據就是能夠提前預測的數據,如通過報名的方式提前篩選出來或通過大數據分析來提前發現熱點商品。動態熱點數據就是系統運行過程中產生的熱點。

 2)發現靜態熱點數據

    靜態熱點數據可以通過提前報名方式篩選出來,也可以通過技術手段提前預測,例如對買家每天訪問的商品進行大數據計算,統計出TOP N的商品,可以認爲這些就是熱點商品。

    發現靜態熱點數據的缺點是時效性較差。

  3)發現動態熱點數據

     發現動態熱點數據的具體實現:

         1.構建一個異步的系統,可以收集交易鏈路上各個環節中的中間件產品的熱點key,如Nginx、緩存、RPC服務框架等。

          2.建立一個熱點上報和可以按照需求訂閱的熱點服務下發規範,目的是通過交易鏈路上各個系統訪問的時間差,把上游已         經發現的熱點透傳給下游系統,提前做好保護。 

         3.  將上游系統收集的熱點數據發送到熱點服務檯,然後下游系統就會知道哪些商品會被頻繁調用,然後做熱點保護。

   4)熱點數據的處理

      處理熱點數據的思路是優化、限制、隔離

      優化:優化熱點數據最有效的辦法就是緩存熱點數據,如果熱點數據做了動靜分離,可以長期緩存靜態數據,但熱點數據更多的是臨時緩存,設置緩存失效時間。

      限制:限制更多的是一種保護機制,例如對商品ID做一致性hash,把熱點商品限制在一個隊列裏,防止熱點商品佔用太多的服務器資源,而使其他請求得不到處理資源。

      隔離:秒殺的第一原則就是將熱點數據隔離出來,不要讓1%的請求影響到另外99%的業務。

          業務隔離:把秒殺做成一種營銷活動,賣家要參加秒殺這種營銷活動需要單獨報名,從技術上來說可以提前做好預熱。

          系統隔離:系統隔離更多的是運行時的隔離,可以通過分組部署的方式和另外 99% 分開。

          數據隔離:秒殺所調用的數據大部分都是熱點數據,比如會啓用單獨的 Cache 集羣或者 MySQL 數據庫來放熱點數據。

 

6.流量削峯   

     削峯的存在可以讓服務器的處理變得更加平穩,可以節省服務器的資源成本。削峯本質上就是更多的延緩用戶請求的發出,以便減少和鍋爐一些無效請求。

     流量削峯的幾種操作思路:排隊、答題、分層過濾。

     1)排隊

        用消息隊列來緩衝瞬時流量,除了消息隊列,類似排隊還有很多方式,如:

         a.利用線程池加鎖等待也是一種常用排隊方式。

        b.先進先出、先進後出等常用的內存排隊算法的實現方式。

        c.把請求序列化到文件中,然後再順序的讀文件來恢復請求等方式。

        這些方式都是把一步的操作變成兩步的操作,起到緩衝的作用。

     2)答題

         增加答題有兩個目的,一個是防止部分買家使用秒殺器在參加秒殺時作弊。另一個目的是延緩請求,起到對請求流量進行削峯的作用,從而讓系統更好的支持瞬時的流量高峯。

         秒殺答題設計思路:

     

    整個答題邏輯主要分爲3部分

       第一,題庫生成模塊,主要是生成一個問題和答案,防止秒殺器來答題。

       第二,題庫的推送模塊,用於在答題前,把題目推送給詳情繫統和交易系統。題庫推送主要爲了保證每次用戶請求的題目是唯一的,防止答題作弊。

       第三,題目的圖片生成模板,用於把題目生成圖片格式,並在圖片裏增加干擾因素。由於答題時網絡擁擠,應該把題目圖片提前推送到CDN上並進行預熱。

   3)分層過濾

   

    核心思想是:在不同的層次儘可能的過濾掉無效請求,讓漏斗最末端的纔是有效請求,要達到這種效果,必須對數據做分層校驗。

     分層校驗原則:

       a.將動態請求的讀數據緩存在web端,過濾掉無效的數據讀。

       b.對讀數據不做強一致性校驗,減少因爲一致性校驗產生瓶頸的問題。

       c.對寫數據進行基於時間的合理分片,過濾掉過期的失效請求。

       d.對寫請求做限流保護,將超出系統承載能力的請求過濾掉。

       e.對寫數據進行強一致性校驗,只保留最後有效的數據。

    分層校驗的目的是:在讀系統中,儘量減少由於一致性校驗帶來的系統瓶頸,儘量將不影響性能的校驗提前。在寫數據系統中,主要對寫的數據做一致性檢查,最後在數據庫層保證數據的最終準確性。

 

7.影響性能的因素

     主要討論影響系統服務端性能的因素,一般用QPS(Query Per Second,每秒請求數)衡量,與QPS相關的一個影響就是響應時間(Response Time,RT),可以理解爲服務器處理響應的耗時。

 

8.如何優化系統

   1)減少編碼   

     java編碼運行比較慢,在很多場景下,涉及字符串的操作都比較耗CPU資源,不管是磁盤I/O還是網絡I/O,都需要將字符轉換成字節,而轉換必須編碼,每個字符的編碼都需要查表,這種查表操作非常耗資源,所以減少字符到字節或相反的轉換、減少字符編碼會非常有效,減少編碼可以大大提升性能。      

   2)減少序列化

     減少java中的序列化操作可以大大提升系統性能。序列化大部分是在 RPC 中發生的,因此避免或者減少 RPC 就可以減少序列化。有一種新的方案,就是可以將多個關聯性比較強的應用進行“合併部署”,而減少不同應用之間的 RPC 也可以減少序列化的消耗。

   3)java極致優化

     ava 和通用的 Web 服務器(如 Nginx 或 Apache 服務器)相比,在處理大併發的 HTTP 請求時要弱一點,所以一般我們都會對大流量的 Web 系統做靜態化改造,讓大部分請求和數據直接在 Nginx 服務器或者 Web 代理服務器上直接返回,而 Java 層只需處理少量數據的動態請求。針對這些請求的優化:

    a.直接使用 Servlet 處理請求,避免使用傳統的 MVC 框架,這樣可以繞過一大堆複雜且用處不大的處理邏輯.

    b.直接輸出流數據,使用 resp.getOutputStream() 而不是 resp.getWriter() 函數,可以省掉一些不變字符數據的編碼,從而提升性能;數據輸出時推薦使用 JSON 而不是模板引擎來輸出頁面。

    4)併發讀優化

    採用應用層的 LocalCache,即在秒殺系統的單機上緩存商品相關的數據。需要劃分成動態數據和靜態數據分別進行處理:

     a.像商品中的“標題”和“描述”這些本身不變的數據,會在秒殺開始之前全量推送到秒殺機器上,並一直緩存到秒殺結束;

     b.庫存這類動態數據,會採用“被動失效”的方式緩存一定時間,失效後再去緩存拉取最新的數據。

 

9.減庫存的幾種方式

  1)下單減庫存:下單減庫存是最簡單的減庫存方式,也是控制最精確的一種,下單時直接通過數據庫的事務機制控制商品庫            存,這樣一定不會出現超賣的情況。

  2)付款減庫存:付款時才減庫存,如果併發比較高,有可能出現買家下單後付不了款的情況,因爲商品可能已經賣出。

  3)預扣庫存:買家下單後,庫存爲其保留一定的時間,超過這個時間,庫存將會自動釋放,釋放後其他買家就可以繼續購買。

  如何減庫存?

    第一種,在應用中通過事務來判斷,保證減後庫存不能爲負數,否則就回滾。

    第二種,直接設置數據庫字段數據爲無符號整數,這樣減後庫存字段值小於零時執行SQL語句會報錯

    第三種,通過SQL的case when語句判斷或樂觀鎖實現。

 

10.減庫存的極致優化

    減庫存需要再數據庫層中完成操作,由於MySQL 存儲數據的特點,同一數據在數據庫裏肯定是一行存儲(MySQL),因此會有大量線程來競爭 InnoDB 行鎖,而併發度越高時等待線程會越多,TPS(Transaction Per Second,即每秒處理的消息數)會下降,響應時間(RT)會上升,數據庫的吞吐量就會嚴重受影響。

    分離熱點商品到單獨的數據庫沒有解決併發鎖的問題,要解決併發鎖有兩種辦法:

    1)應用層做排隊。按照商品維度設置隊列順序執行,這樣能減少同一臺機器對數據庫同一行記錄進行操作的併發度,同時也能控制單個商品對數據庫連接的數據,防止熱點商品佔用太多的數據庫連接。

    2)數據庫層做排隊。應用層只能做到單機排隊,控制併發的能力有限,如果能在數據庫層做全局排隊是最理想的。阿里針對這種MySQL的innoDB層上的補丁程序(patch),可以再數據庫層上對單行記錄做到併發排隊。

 

11.遇到大流量時,如何保障系統的穩定運行

   1)降級

    當系統的容量達到一定程度時,限制或者關閉系統的某些非核心功能,從而把有限的資源保留給更核心的業務。它是一個有目的、有計劃的執行過程,所以對降級我們一般需要有一套預案來配合執行。如果我們把它系統化,就可以通過預案系統和開關係統來實現降級。

    執行降級無疑是在系統性能和用戶體驗之間選擇了前者,降級後肯定會影響一部分用戶的體驗。所以降級的核心目標是犧牲次要的功能和用戶體驗來保證核心業務流程的穩定,是一個不得已而爲之的舉措。

    2)限流

    限流就是當系統容量達到瓶頸時,我們需要通過限制一部分流量來保護系統,並做到既可以人工執行開關,也支持自動化保護的措施。限流可以再客戶端,也可以再服務端。

    客戶端限流,好處是可以限制請求的發出,通過減少發出無用請求從而減少對系統的消耗。缺點是當客戶端比較分散時,沒法設置合理的限流閾值,如果閾值設置的太小,會導致服務端沒有達到瓶頸時客戶端已經被限制,如果設置的太大,起不到限制的作用。

     服務端限流,好處是可以根據服務端的性能設置合理的閾值,缺點是被限制的請求都是無效的請求,處理這些無效請求本身也會消耗服務器資源。

      限流會影響用戶的正常請求,必然會導致一部分用戶請求失敗,因此在系統處理這種異常時一定要設置超時時間,防止因被限流的請求不能fast fail(快速失敗)而拖垮系統。

     3)拒絕服務

      當系統負載達到一定閾值時,例如 CPU 使用率達到 90% 或者系統 load 值達到 2*CPU 核數時,系統直接拒絕所有請求,這種方式是最暴力但也最有效的系統保護方式。拒絕服務用以防止最壞情況發生,防止因把服務器壓垮而長時間徹底無法提供服務,像這種系統過載保護雖然在過載時無法提供服務,但是系統仍然可以運作,當負載下降時又很容易恢復,所以每個系統和每個環節都應該設置這個兜底方案,對系統做最壞情況下的保護。

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