設計原因:
爲什麼要針對秒殺設計一個完善的方案?因爲系統可能會因爲1%的秒殺業務影響其餘99%正常業務的運行,所以需要將秒殺系統獨立出來。
待解決問題:
主要解決兩個問題:併發讀、併發寫。
整體架構要求:
概括爲:"穩、準、快",即對應"高可用、一致性、高性能",其介紹分別如下:
高可用:保證系統的高可用和正確性,設計PlanB進行兜底。
一致性:保證秒殺減庫存中的數據一致性。
高性能:涉及大量併發讀寫,所以需要支持高併發,從動靜分離、熱點發現與隔離、請求削峯與分層過濾、服務端極致優化來介紹。
五個架構原則(4要1不要):
數據儘量少:包含請求和響應的數據,因爲網絡傳輸需要時間。可簡化秒殺頁面、減少數據庫打交道。
請求數儘量少:瀏覽器渲染頁面包含額外請求,比如以來了css、js、圖片等定義爲額外請求,這些請求儘可能少,若請求域名不一致,還涉及DNS解析,耗時更久,可採用合併方法如:http://xxx.com/tm/modes??module-preview/index.xtpl.js,module-jhs/index.xtpl.js ,這個在服務器依舊是單獨存放,只是提供了一個組件解析這個url。比如以下是淘寶的一個靜態資源鏈接:
路徑儘量短:這裏的路徑表示節點。每個節點都會產生新的socket連接,一個節點的可用性是99.9%,經過五個節點後就是五次方爲99.5%。方法可以是服務內部RPC調用變爲jvm內部調用。
依賴儘量少:將服務分級,比如將支付服務作爲0級,優惠券是1級,要避免0級系統被1級系統拖垮。
不要有單點:單點意味着沒有備份。將服務無狀態化,即避免服務與機器綁定。可設置配置中心映射。
注:數據、請求數、路徑、依賴、單點。但是其實這些原則會產生衝突,如請求數量少中的合併請求會使單次請求的數據量變大,這與數據儘量少違背。所以需要我們做一個平衡。
秒殺系統的大體涉及結構:
1 頁面徹底動靜分離,使得用戶秒殺時不需要刷新整個頁面,降低刷新請求數。
2 服務器緩存秒殺商品,直接調用緩存層,無需穿透到數據庫層找數據。
3 增加流量限流保護,防止最g壞情況。
如何動靜分離:
把用戶請求數據(如HTML)分爲"動態數據"和"靜態數據"。
動態數據與靜態數據區別:確定輸出的數據是否含訪問者個性化數據如 個人信息、cookie等私密數據。
區別動靜數據的作用:區分了動靜數據,就可以將靜態數據緩存,提高效率。
如何對靜態數據做緩存:
1. 將靜態數據放在離用戶最近的地方,如CDN、用戶瀏覽器、服務端的Cache。
2. 做靜態化改造,直接緩存HTTP連接。Web服務器根據請求URL直接取出HTTP響應頭、響應體直接返回。
3. 選擇誰來緩存靜態數據,如可在web服務器層做緩存,屏蔽java層的弱點,不在java層做緩存。
如何做動靜分離改造:
1. URL唯一化。如每個商品鏈接爲:http://item.xxx/xxxx?id=xxx 來作爲緩存的key,用於緩存整個HTTP連接。
2. 分離請求,含用戶信息相關、時間、地獄相關,這些都可以通過異步請求獨立獲取。
3. 服務器返回的信息可去掉cookie。如Vanish可用unset req.http.cookie去掉cookie。
動態內容處理方案:
ESI:web代理服務器做動態請求時,將動態內容插入靜態頁面,然後全部返回。
CSI:異步js請求,性能最佳,但是有一定時延,可能對用戶體驗不好。
動靜分離的幾種架構方案:
1. 實體機單機部署:將虛擬機運行的Java應用換成實體機
優點:無網絡瓶頸、可使用大內存。
提升命中率,減少Gzip壓縮。
減少cache失效壓力,因爲採用定時失效,如3分鐘失效。
2 統一Cache層:即抽離cache出來作爲一個獨立的集羣。可設置二級Cache,放置回原(原服務器)
3. 上CDN:可增加二級Cache防止回原(原服務器)。
二八原則(針對性處理熱點數據):
爲什麼要針對性處理:熱點數據會大量佔用服務資源,0.1%業務會搶佔系統90%以上的資源。
什麼是熱點:
熱點分爲熱點操作和熱點數據,其中熱點操作是一種優化的方式,將會在秒殺的操作優化講解。
熱點數據分爲動態、靜態熱點數據如下:
-靜態熱點數據即可提前預測數據如數據分析出哪種更熱門、一個商品做活動等。
-動態熱點數據不可預測,如抖音廣告然後突然火了。
如何發現熱點數據:
發現靜態熱點數據:強制讓賣家通過報名方式提前把熱點數據篩選出來緩存,但是增加了賣家的工作量,也不夠實時。也可以根據每日訪問數進行統計,然後緩存TOP N的商品。
發現動態熱點數據:抽離出一箇中間件用於收集搜索、商品詳情、購物車等關鍵熱點業務的點擊數據,然後異步記錄到日誌,然後根據規則判斷是否熱點數據後緩存在隊列中(因爲熱點數據一般是臨時的,所以可採用LRU算法淘汰)。
流量削峯怎麼做:
爲什麼要削峯:穩定服務端,節省資源,本質是延緩用戶請求發出,減少和過濾無用請求(遵循請求書儘量少原則)。
削峯思路:排隊、答題、分層過濾
排隊:消息隊列緩存大量併發,把原來的一步操作變成兩步。雖然違背了增加訪問路徑原則,但是防止了系統崩潰。
答題:可防止爬蟲等的自動搶購的腳本。延緩請求從之前的1s內延緩到2-10s,對事件進行了分片,減緩服務器壓力,如微信的搖一搖、支付寶休一休。也可限制答題時間間隔。
分層過濾:分層爲:CDN->前臺讀系統(商品詳情繫統)->後臺寫系統(交易系統)->DB
大部分數據和流量都在CDN獲取,攔截了大部分讀的數據。
經過第二層(前臺讀系統)儘量走Cache。
到第三層(後臺寫系統),做數據校驗、限流,進一步減少數據量和請求。
最後在數據層完成強一致性校驗。
分層過濾核心思想:各層過濾無效請求,所以必須對數據做分層校驗,其校驗原則如下:
將動態請求的讀數據緩存在瀏覽器本地,過濾無效數據讀。
對讀數據不做強一致性校驗,較少一致性校驗帶來的性能問題。
對寫數據基於時間的合理分片,過濾過期失效請求。
對寫數據做強一致性校驗,只保留有效數據。
影響性能的因素及其可優化處:
影響服務端性能的因素:QPS、響應時間(RT)
計算公式:QPS = (1000ms / 響應時間) * 線程數量,真正影響性能的是CPU執行時間。
再來分析下線程數是否對QPS的影響:不是線程數越多,QPS越高,因爲線程上下文切換有消耗。所以需要合理的設置線程數,一般的計算公式爲:線程數 = [(線程等待時間 + 線程CPU時間) / 線程CPU時間] * CPU數量,當然最好的方式是性能測試來確認。
如何發現瓶頸:就緩存系統而言,制約的是內存。存儲系統的瓶頸是I/O。
秒殺系統的大部分瓶頸在CPU(使用JProfiler、YourKit),但不一定是CPU,有可能是其他部分,比如QPS達到極限時,CPU使用率是否超過95%,如果不是則可能是鎖限制或過多本地I/O等待發生。
如何優化系統:
減少編碼:java編碼速度慢,涉及字符串操作(輸入輸出操作、I/O操作)比較消耗CPU資源。原因是磁盤、網絡IO都需要將字符串轉爲字節,這個轉換必須查表編碼。可通過(OutputStream()直接進行流輸出),可提高30%.
減少序列化:序列化與編碼同時發生,所以需要減少。儘量減少RPC,將關聯性強的應用服務合併。
Java極致優化:對大流量Web系統做靜態化改造;直接使用Servlet,繞過框架多餘處理邏輯;直接輸出流數據。
併發讀優化:秒殺系統單機緩存。不要求讀一致性,但是寫數據的時候要求強一致性。
秒殺系統設計的核心邏輯:
秒殺系統最重要要求是 "不要超賣",關鍵在於減庫存。
減庫存方式(三種):
下單減庫存:一定不會出現超賣情況,但是有些人下單完不付款會影響其他人。
付款減庫款:付款減庫存,可能會因爲併發高導致付款時已經賣光,付不了款。
預扣庫存:最常用,如下單後扣庫存,保留十分鐘,在十分鐘內未付款就不保留。如果付款時發現庫存不足則不允許付款。
減庫存存在的問題:在下單減庫存、預扣庫存的情況下,有競爭對手惡意多賬號下單導致庫存降爲0,那商品就無法正常賣。
解決辦法:指定反作弊措施,如給經常下單不買的用戶打標識、設置最大購買書、設置重複下單不付款操作數。
秒殺減庫存極致優化:
秒殺商品減庫存放緩存如redis。給熱點商品提供獨立的緩存層、DB層集羣。使用排隊如應用層排隊、數據庫層排隊(阿里針對mysql的innodb做了補丁程序patch可對單行記錄做併發排隊)解決數據庫併發鎖問題。
設計Plan B兜底方案:
再牛逼的系統也會問題,比如超大流量導致宕機,出現最壞情況,所以需要設計Plan對應高可用性。
各階段處理操作:
架構階段:考慮拓展性和容錯性,避免系統出現單點問題。
編碼階段:保證代碼健壯性,合理設置超時退出機制,對於異常捕獲後需要一個默認處理。
測試階段:保證最壞情況下,也有相應處理流程。
發佈階段:需要有備份用於回滾。
運行階段:系統監控和報警,如cat監控系統。
故障發生:及時止損,如下架標錯價商品。
運行階段詳細處理:
降級:限制或關閉某些非核心功能,留給核心功能。如展示成功記錄由30條變成5條。
限流:設置一個QPS閾值,達到則排隊或丟棄。
拒絕:當連接數過大,cpu負載達到90%,就拒絕請求。
讀自:許令波-《如何設計一個秒殺系統》
---------------------
作者:Lidisam
來源:CSDN
原文:https://blog.csdn.net/qq_28666081/article/details/83043215
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!