秒殺這個話題到現在來說已經是景設計一個老生常談的話題了,不過因為又臨近一年一度的高頻雙11,而且發現前段時間無論是面試阿里還是騰訊一些大廠其實還是在頻繁的問到這個場景題,所以還是題秒準備拿出來說說。
秒殺從規模上來說可以分為大秒和小秒。殺場大秒指的景設計是比如雙11這種特定的節日,商品規模超大、高頻價格超低、面試流量超大的題秒這種類型活動,小秒一般指的殺場是商家自己配置的一些時段類型的活動,由商家自己指定時間上架。景設計從形式來說還可以分為單時段秒殺和多時段秒殺。但是在這個場景里,我們一般就是指的單時段大型秒殺。
秒殺設計要面對的壓力和難度有幾點:
怎么保證超高的流量和并發下系統的穩定性?如果峰值的QPS達到幾十萬,面對巨大的流量的壓力系統怎么設計保證不被打崩?
怎么保證數據最終一致性?比如庫存不能超賣,超賣了那虧本的要么就是商家要么就是平臺,用戶反正不背這個鍋,超賣了就今年325預訂。
當然,涉及到這種大型的活動,還需要考慮到數據統計分析,總不能活動做完了,效果不知道怎么樣。
系統架構
假設今年的雙11預估峰值QPS將會有50萬(我隨便扯的),而根據我們平時的經驗單機8C8G的機器可以達到1000左右的QPS,那么從理論上來說我們只要500臺機器就可以抗住了,就有錢任性不行?這么設計的話只能出門右轉不送了。
流量過濾
本質上,參與秒殺的用戶很多,但是商品的數量是有限的,真正能搶到的用戶并不多,那么第一步就是要過濾掉大部分無效的流量。
活動開始前前端頁面的Button置灰,防止活動未開始無效的點擊產生流量 前端添加驗證碼或者答題,防止瞬間產生超高的流量,可以很好的起到錯峰的效果,現在的驗證碼花樣繁多,題庫有的還要做個小學題,而且題庫更新頻繁,想暴力破解怕是很難。當然我知道的還有一種人工打碼的方式,不過這個也是需要時間的,不像機器無限刷你的接口。 活動校驗,既然是活動,那么活動的參與用戶,參加條件,用戶白名單之類的要首先做一層校驗攔截,還有其他的比如用戶終端、IP地址、參與活動次數、黑名單用戶的校驗。比如活動主要針對APP端的用戶校驗,那么根據參數其他端的用戶將被攔截,針對IP、mac地址、設備ID和用戶ID可以對用戶參與活動的次數做校驗,黑名單根據平時的活動經驗攔截掉一部分羊毛黨等異常用戶。 非法請求攔截,做了以上攔截如果還有用戶能繞過限制,那不得不說太牛X了。比如雙11零點開始還做了答題限制,那么正常人怎么也需要1秒的時間來答題吧,就算單身30年手速我想也不能超過0.5秒了,那么針對剛好0點或者在0.5秒以內的請求就可以完全攔截掉。 限流,假設秒殺10000件商品,我們有10臺服務器,單機的QPS在1000,那么理論上1秒就可以搶完,針對微服務就可以做限流配置,避免后續無效的流量打到數據庫造成不必要的壓力。針對限流還有另外一種柵欄方式限流,這是一種純靠運氣的限流方式,就是在系統約定的請求開始的時間內隨機偏移一段時間,針對每個請求的偏移量不同,如果在偏移時間之內就會被攔截,反之通過。
性能優化
做完無效流量的過濾,那么可能你的無效請求已經過濾掉了90%,剩下的有效流量會大大的降低系統的壓力。之后就是需要針對系統的性能做出優化了。
頁面靜態化,參與秒殺活動的商品一般都是已知的,可以針對活動頁面做靜態化處理,緩存到CDN。假設我們一個頁面300K大小,1千萬用戶的流量是多少?這些請求要請求后端服務器、數據庫,壓力可想而知,緩存到CDN用戶請求不經過服務器,大大減小了服務器的壓力。 活動預熱,針對活動的活動庫存可以獨立出來,不和普通的商品庫存共享服務,活動庫存活動開始前提前加載到redis,查詢全部走緩存,最后扣減庫存再視情況而定。 獨立部署,資源充足的情況下可以考慮針對秒殺活動單獨部署一套環境,這套環境中可以剝離一些可能無用的邏輯,比如不用考慮使用優惠券、紅包、下單后贈送積分的一些場景,或者這些場景可以活動結束后異步的統一發放。這只是一個舉例,實際上單獨針對秒殺活動的話你肯定有很多無用的業務代碼是可以剝離的,這樣可以提高不少性能。
經過這兩步之后,最終我們的流量應該是呈漏斗狀。
超賣
秒殺除開高并發高流量下的服務穩定性之外,剩下的核心大概就是怎么保證庫存不超賣了,也可以說要保證的是最終一致性。一般來說,針對下單和庫存有兩種方式:
下單即扣庫存,這是最常規的大部分的做法。但是可能在活動中會碰到第二點說到的情況。
支付完成扣庫存,這種設計我碰到過就是酒店行業,廉價房放出來之后被黃牛下單搶占庫存導致正常用戶無法下單,然后黃牛可以用稍高的價格再售賣給用戶從中牟利,所以會有在一些活動的時候采取支付成功后才占用庫存的做法。不過這種方式實現起來比較復雜,可能造成大量的無效訂單,在秒殺的場景中不太適用。
針對秒殺建議選擇下單扣庫存的方式,實現相對簡單而且是常規做法。
方案
首先查詢redis緩存庫存是否充足 先扣庫存再落訂單數據,可以防止訂單生成了沒有庫存的超賣問題 扣庫存的時候先扣數據庫庫存,再扣減redis庫存,保證在同一個事務里,無論兩者哪一個發生了異常都會回滾。有一個問題是可能redis扣成功了由于網絡問題返回失敗,事務回滾,導致數據庫和緩存不一致,這樣實際少賣了,可以放到下輪秒殺去。
這種做法能一定程度上解決問題,但是也有可能會有其他問題。比如當大量請求落在同一條庫存記錄上去做update時,行鎖導致大量的鎖競爭會使得數據庫的tps急劇下降,性能無法滿足要求。
另外一種做法就是排隊,在服務層進行排隊,針對同一個商品ID的也就是數據庫是一條庫存記錄的做一個內存隊列,串行化去扣減庫存,可以一定程度上緩解數據庫的并發壓力。
質量保障
為了保證系統的穩定性,防止你的系統被秒殺,一些質量監控就不得不做。
熔斷限流降級,老生常談,根據壓測情況進行限流,可以使用sentinel或者hystrix。另外前端后端都該有降級開關。 監控,該上的都上,QPS監控、容器監控、CPU、緩存、IO監控等等。 演練,大型秒殺事前演練少不了,不能冒冒失失的就上了吧。 核對、預案,事后庫存訂單 金額、數量核對,是否發生超賣了?金額是否正常?都是必須的。預案可以在緊急情況下進行降級。
數據統計
活動做完了,數據該怎么統計?
前端埋點 數據大盤,通過后臺服務的打點配合監控系統可以通過大盤直觀的看到一些活動的監控和數據 離線數據分析,事后活動的數據可以同步到離線數倉做進一步的分析統計
總結
總的來說,面對巨量的流量我們的方式就是首先通過各種條件先篩選掉無效流量,進行流量錯峰,然后再對現有的系統性能做出優化,比如頁面靜態化,庫存商品預熱,也可以通過獨立部署的方式和其他的環境做隔離,最后還要解決高并發下緩存一致性、庫存不能超賣的問題,防止大量的并發打爆你的數據庫。
一個完整的活動從前端到后端是一個完整的鏈路,中間有事前的演練工作,事后的數據分析等都是必不可少的環節。
- END -
特別推薦一個分享架構+算法的優質內容,還沒關注的小伙伴,可以長按關注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!