秒殺系統架構設計

 1,秒殺場景核心是要保證三點:穩,準,快;對應的技術落地即是高可用,一致性,高性能;

 2,架構設計五原則

         2.1 數據要儘可能少:網絡傳輸耗時,服務器壓縮以及字符編碼,序列化與反序列化消耗cpu

         2.2 請求數要儘可能少:頁面依賴的 CSS/JavaScript、圖片,以及 Ajax,創建鏈接要作三次握手

         2.3 路徑要儘可能短:縮短請求路徑不只能夠增長可用性,一樣能夠有效提高性能(減小中間節點能夠減小數據的序列化與反序列化),並減小延時(能夠減小網絡傳輸耗時)

         2.4 依賴要儘可能少: 完成一次用戶請求必須依賴的系統或者服務,這裏的依賴指的是強依賴,能夠給系統進行分級。

         2.5 不要有單點:單點意味着沒有備份,風險不可控。避免將服務的狀態和機器綁定,即把服務無狀態化,把和機器相關的配置動態化;存儲服務通常要經過冗餘多個備份的方式來解決單點問題。

           優化的點:秒殺系統單獨獨立出來;獨立作一個機器集羣;將熱點數據(如庫存數據)單獨放到一個緩存系統中,以提升「讀性能「 ;增長秒殺答題,防止有秒殺器搶單;頁面進行完全的動靜分離,使得用戶秒殺時不須要刷新整個頁面;在服務端對秒殺商品進行本地緩存,不須要再調用依賴系統的後臺獲取;增長系統限流保護,防止最壞狀況發生。前端

 3,如何作好動靜分離,有哪些方案。

         3.1 概念:「動態數據」和「靜態數據」的主要區別就是看頁面中輸出的數據是否和 URL、瀏覽者、時間、地域相關,以及是否含有Cookie 等私密數據。

         3.2 怎樣對靜態數據作緩存:瞭解了動靜數據的概念後,咱們很容易想到對靜態數據作緩存用來提升系統性能。

                 1,把靜態數據緩存到離用戶最近的地方,緩存到用戶瀏覽器裏、CDN 上或者在服務端的 Cache 中java

                 2,靜態化改造就是要直接緩存 HTTP 鏈接。mysql

                 3,讓誰來緩存靜態數據也很重要,緩存能夠放在Web 服務器(如 Nginx、Apache、Varnish)更擅長處理大併發的靜態文件請求。redis

         3.3 方案:1,實體機單機部署;2,統一cache層;3,上CDN

 4,熱點數據的處理:二八原則

         4.1  概念:熱點分爲熱點操做和熱點數據。所謂「熱點操做」,例如大量的刷新頁面、大量的添加購物車;熱點數據」比較好理解,那就是用戶的熱點請求對應的數據。而熱點數據又分爲「靜態熱點數據」和「動態熱點數據」。

         4.2 靜態熱點數據」,就是可以提早預測的熱點數據。例如,咱們能夠經過賣家報名的方式提早篩選出來,經過報名系統對這些熱點商品進行打標。另外,咱們還能夠經過大數據分析來提早發現熱點商品,好比咱們分析歷史成交記錄、用戶的購物車記錄,來發現哪些商品可能更熱門、更好賣,這些都是能夠提早分析出來的熱點。

         4.3 動態熱點數據」,就是不能被提早預測到的,系統在運行過程當中臨時產生的熱點。例如,賣家在抖音上作了廣告,而後商品一下就火了,致使它在短期內被大量購買。

         4.4 發現熱點數據:

                  4.4.1 發現靜態熱點數據:強制賣家報名,活動商品打標;大數據計算統計top N 商品算法

                  4.4.2 發現動態熱點數據:1,構建一個異步的系統,它能夠收集交易鏈路上各個環節中的中間件產品的熱點 Key;2,創建一個熱點上報和能夠按照需求訂閱的熱點服務的下發規範;3,將上游系統收集的熱點數據發送到熱點服務檯,而後下游系統(如交易系統)就會知道哪些商品會被頻繁調用,而後作熱點保護。sql

          4.5  處理熱點數據:

                  4.5.1 優化:緩存起來,而後能夠採用 LRU 淘汰算法替換。數據庫

                  4.5.2 限制:例如對被訪問商品的 ID 作一致性 Hash,而後根據 Hash 作分桶,每一個分桶設置一個處理隊列,這樣能夠把熱點商品限制在一個請求隊列裏,防止因某些熱點商品佔用太多的服務器資源,而使其餘請求始終得不到服務器的處理資源。瀏覽器

                  4.5.3 隔離緩存

                          4.5.3.1 業務隔離:開賣前,賣家單獨報名,對熱點數據提早作預熱安全

                          4.5.3.2 系統隔離:能夠經過分組部署的方式和另外 99% 分開,殺能夠申請單獨的域名,目的也是讓請求落到不一樣的集羣中。

                          4.5.3.3 數據隔離:秒殺所調用的數據大部分都是熱點數據,好比會啓用單獨的 Cache 集羣或者 MySQL 數據庫來放熱點數據,目的也是不想0.01% 的數據有機會影響 99.99% 數據。

 5,流量削峯這事應該怎麼作?

            5.1 爲何要削峯

                        服務器的處理資源是恆定的,爲了用戶體驗,咱們必須按照用戶請求的最大峯值進行預分配機器嗎,但這樣會大大增長服務器成本;因此削峯一方面是爲了下降成本,一方面是爲了系統的穩定性。

            5.2 有哪些手段

                     5.2.1 排隊:經過使用消息中間件(做用:異步,解耦,削峯填谷)把請求平滑緩衝入隊列中,能夠承接瞬間過來的大流量,避免瞬時大流量請求致使系統崩潰。而後業務系統再去處理隊列中的請求。若是瞬時流量過大,達到了消息隊列處理的上限,這時候請求可能會被直接丟棄。咱們能夠採用把請求序列化到文件中來進行處理(相似於mysql bin log的方式),後期再從文件中讀取請求進行進一步的處理。

                     5.2.2 答題: 1,能夠防止秒殺器  2,能夠把請求基於時間分片,下降瞬時請求併發率。

                     5.2.3 分層過濾:在不一樣的層次儘量地過濾掉無效請求,讓「漏斗」最末端的纔是有效的請求。總之就是讀不作強一致性校驗,寫時須要作一致性檢查。很是適合交易性的寫請求,好比減庫存或者拼車等,拼車時座位是一直在變化的,不必定要保證讀到的必定是準確的,只須要在寫數據時進行強一致性檢驗便可。

                     5.2.4 業務手段:除了採用技術手段解決,還能夠採用業務手段。好比秒殺時,能夠經過發放優惠券或者開啓抽獎活動等吸引一部分買家到其餘地方,也能夠起到緩存流量的做用。

 6,影響性能的因素有哪些,又該如何提升系統的性能

            6.1 影響性能的因素:

                     這裏主要討論服務端性能。主要因素:響應時間、線程數

                     qps:每秒處理的請求數。

                      6.1.1 響應時間對qps的影響

                     響應時間 = cpu執行時間+線程等待時間(好比 RPC、IO 等待、Sleep、Wait)

                     真正對cpu有影響的是cpu的執行時間,咱們應該致力於減小 CPU 的執行時間。

                      6.1.2 線程數對qps的影響

                     線程並不是越多越好,線程自己也消耗資源,也受到其餘因素的制約,好比線程切換成本高,每一個線程也會耗費必定的內存。

                     最佳實踐計算公式:線程數 = [(線程等待時間 + 線程 CPU 時間)/線程 CPU 時間] × CPU 數量,最好的辦法是經過性能測試來發現最佳的線程數。

            6.2 如何發現瓶頸

                     首先,秒殺場景的瓶頸更多的發生在cpu上。

                     6.2.1 經過cpu診斷工具:JProfiler、Yourkit

                     6.2.2 經過jstack定時打印調用棧,若是某些函數調用頻繁或者耗時較多,這些函數就會出如今系統調用棧裏,經過採樣的方式定時發現耗時較多的函數

                     6.2.3 怎麼判斷cpu有沒有達到瓶頸:當QPS達到極限時,判斷cpu使用率有沒有超過95%

            6.3 如何優化

                     6.3.1 減小編碼:編碼查表很是耗費資源

                     6.3.2 減小序列化:序列化大部分發生在RPC調用當中,應該避免或減小RPC調用,能夠將兩個關聯性比較強的應用合併部署到同一臺機器,使用同一個TOMCAT,且不能走本機的SOCKET。

                     6.3.3 java極致優化  1,首先作靜態化改造,讓大部分的請求在NIGNX服務器或WEB服務器上直接返回;2,使用Servlet處理請求,避免使用mvc框架,能夠繞過一些無用的處理邏輯(取決於項目對框架的依賴);3,直接輸出流數據。響應時推薦使用JSON,而不是模板引擎(通常都是解釋執行)。

                     6.3.4 併發讀優化:集中式緩存爲了提升命中率通常會採用一致性hash的策略。但還不足以處理大秒,能夠採用應用層的localcache,在秒殺系統的單機上緩存商品相關的數據,因此還須要動靜數據的分離。靜態數據全量推送上去,動態數據採用被動失效的方式緩存一段時間,失效後再去主動緩存拉取最新的數據。

 7,秒殺系統「減庫存」設計的核心邏輯

               咱們使用電商平臺購物通常都會涉及兩個核心流程:下訂單、付款,那麼咱們應該是在用戶下訂單的時候減庫存仍是在實際付款後再進行減庫存操做呢?接下去咱們分析一下各個方案的優缺點,以及存在的問題。

             7.1 減庫存的三種方式及各自存在的問題

                      7.1.1  下單減庫存:這種方式必定不會出現超賣,但可能出現客戶下完單,不付款的狀況。若是存在惡意用戶大量下單,卻不付款,這種方式很快就會把庫存減爲0,致使商品不能正常售賣。

                      7.1.2  付款減庫存:會產生大量客戶下完單,付款時卻不成功的狀況,庫存超賣,致使客戶體驗較差。

 

                      7.1.3  預扣庫存: 用戶下單後,庫存爲其保存一段時間(如10分鐘),超時未付款後,庫存自動釋放,釋放後其餘買家能夠繼續購買。在買家付款前,系統會檢驗該訂單的庫存是否還有保留:1,若是保留成功,則完成付款則實際減去庫存,2,若是沒有保留,則再次嘗試預扣;若是庫存不足(就是預扣失敗),則不容許付款。若是預扣成功,則完成付款並實際減去庫存。   

             7.2 預扣庫存仍存在問題

                      7.2.1  問題: 預扣庫存的方式,雖然能夠在必定程度上解決上面的問題,但沒法完全解決。針對惡意下單這種狀況,雖然把有效的付款時間設置爲 10 分鐘,可是惡意買家徹底能夠在 10 分鐘後再次下單,或者採用一次下單不少件把庫存減完。

                     7.2.2  解決方案:解決辦法仍是要結合安全和反做弊的措施來制止。例如給常常下班不付款的用戶打標(能夠在被打標的用戶下單時不減庫存^-^),給某些類目設置最大購買件數(例如活動商品一次只能購買三件),以及對重複下單不付款的操做進行次數限制。

             7.3 大型秒殺中怎樣減庫存 

                       普通業務系統會採用預扣庫存的方式,秒殺系統由於大部分人都是抱着「搶到就是賺到的心態」,不多會下單不付款。並且秒殺中商家通常不容許超賣,另外邏輯簡單,在性能上也更有優點。下單減庫存,在數據一致性上主要表現爲保證大併發時庫存不能爲負數,通常採用事務控制,保證減後不爲負數,其次能夠採用庫存字段設置爲無符號整數,這樣減爲負數時會拋出sql錯誤;再有一種是使用case when語法,UPDATE item SET inventory = CASE WHEN inventory >= xxx THEN inventory-xxx ELSE inventory END

             7.4 秒殺減庫存的極致優化   

                      7.4.1 庫存在交易系統中很明顯是熱點數據。大併發下單的讀能夠採用localcache(即在秒殺系統的單機上緩存商品相關的數據)和對數據進行分層過濾的方式,可是大併發寫時不管如何都避免不了的。

                      7.4.2 若是業務邏輯沒有複雜的SKU庫存和總庫存這種聯動關係的話,徹底能夠直接放在帶有持久化功能的緩存系統(如redis)中,若是有比較複雜的邏輯或者須要使用事務,仍是放在數據庫中操做比較好。

                      7.4.2 另外就是單個熱點商品會影響整個數據庫的性能,致使0.01%影響99.99%的商品的售賣,能夠採用把熱點商品單獨放在熱點庫中,這種須要作熱點數據的動態遷移以及單獨的數據庫。

                      7.4.3 要解決併發鎖的問題有兩種方案:1,應用層作排隊,2,數據庫層作排隊

 8,準備Plan B,如何設計兜底計劃

              8.1 高可用建設從哪裏着手(6個階段)

                    8.1.1 架構階段:異步容災,異步化,分組隔離,避免單點

                    8.1.2 編碼階段:超時處理,錯誤捕獲,限流保護,異步線程

                    8.1.3 測試階段:beta測試,自動化對比測試 ,保證測試用例的覆蓋度,最壞狀況下也有相應的處理流程。

                    8.1.4 發佈階段:分批發布,多版本發佈,有緊急的回滾機制

                    8.1.5 運行階段:實時監控報警,過載保護,自動降級,數據對帳

                    8.1.6 故障發生:快速恢復,故障定位

              8.2 秒殺系統高可用方案:

                    8.2.1 降級:配置一套系統化預案與開關係統(如臨時下架優惠券系統)

                    8.2.2 限流:限制一部分流量保護系統。分爲:客戶端限流與服務端限流。限流的實現方式既要支持URL以及方法級別的限流又要支持基於QPS與線程的限流(經過壓測獲取系統最大QPS,好比10000,咱們能夠設置8000來進行限流保護)。限流時會致使用戶請求失敗必定要設置超時,防止因被限流的請求因不能fast fail快速失敗而拖垮系統。

                    8.2.3 拒絕服務:當系統負載達到必定的閾值時,例如 CPU 使用率達到 90% 或者系統 load 值達到2*CPU 核數時,系統直接拒絕全部請求。在最前端的 Nginx 上設置過載保護,當機器負載達到某個值時直接拒絕 HTTP 請求並返回 503 錯誤碼,在 Java 層一樣也能夠設計過載保護。這樣設計在系統負載太高時不提供服務用來保護系統,當負載降低時又能夠很容易恢復。

                    8.2.4 總結:網站的高可用建設是基礎,能夠說要深刻到各個環節,更要長期規劃,並進行體系化建設,要在預防(創建常態的壓力體系,例如上線前的單機壓測到上線後的全鏈路壓測)、管控(作好線上運行時的降級、限流和兜底保護)、監控(創建性能基線來記錄性能的變化趨勢以及線上機器的負載報警體系,發現問題及時預警)和恢復體系(遇到故障要及時止損,並提供快速的數據訂正工具等)等這些地方增強建設,每個環節可能都有不少事情要作。

相關文章
相關標籤/搜索