秒殺這個話題到現(xiàn)在來說已經(jīng)是一個老生常談的話題了盲链,不過因為又臨近一年一度的雙11拙毫,而且發(fā)現(xiàn)前段時間無論是阿里還是騰訊一些大廠其實還是在頻繁的問到這個場景題依许,所以還是準(zhǔn)備拿出來說說。
秒殺從規(guī)模上來說可以分為大秒和小秒缀蹄。大秒指的是比如雙11這種特定的節(jié)日峭跳,商品規(guī)模超大、價格超低缺前、流量超大的這種類型活動坦康,小秒一般指的是商家自己配置的一些時段類型的活動,由商家自己指定時間上架诡延。從形式來說還可以分為單時段秒殺和多時段秒殺。但是在這個場景里古胆,我們一般就是指的單時段大型秒殺肆良。
秒殺設(shè)計要面對的壓力和難度有幾點:
怎么保證超高的流量和并發(fā)下系統(tǒng)的穩(wěn)定性?如果峰值的QPS達(dá)到幾十萬逸绎,面對巨大的流量的壓力系統(tǒng)怎么設(shè)計保證不被打崩惹恃?
怎么保證數(shù)據(jù)最終一致性?比如庫存不能超賣棺牧,超賣了那虧本的要么就是商家要么就是平臺巫糙,用戶反正不背這個鍋,超賣了就今年325預(yù)訂颊乘。
當(dāng)然参淹,涉及到這種大型的活動,還需要考慮到數(shù)據(jù)統(tǒng)計分析乏悄,總不能活動做完了浙值,效果不知道怎么樣。
系統(tǒng)架構(gòu)
假設(shè)今年的雙11預(yù)估峰值QPS將會有50萬(我隨便扯的)檩小,而根據(jù)我們平時的經(jīng)驗單機(jī)8C8G的機(jī)器可以達(dá)到1000左右的QPS开呐,那么從理論上來說我們只要500臺機(jī)器就可以抗住了,就有錢任性不行?這么設(shè)計的話只能出門右轉(zhuǎn)不送了筐付。
流量過濾
本質(zhì)上卵惦,參與秒殺的用戶很多,但是商品的數(shù)量是有限的瓦戚,真正能搶到的用戶并不多沮尿,那么第一步就是要過濾掉大部分無效的流量。
- 活動開始前前端頁面的Button置灰伤极,防止活動未開始無效的點擊產(chǎn)生流量
- 前端添加驗證碼或者答題蛹找,防止瞬間產(chǎn)生超高的流量,可以很好的起到錯峰的效果哨坪,現(xiàn)在的驗證碼花樣繁多庸疾,題庫有的還要做個小學(xué)題,而且題庫更新頻繁当编,想暴力破解怕是很難届慈。當(dāng)然我知道的還有一種人工打碼的方式,不過這個也是需要時間的忿偷,不像機(jī)器無限刷你的接口金顿。
- 活動校驗,既然是活動鲤桥,那么活動的參與用戶揍拆,參加條件,用戶白名單之類的要首先做一層校驗攔截茶凳,還有其他的比如用戶終端嫂拴、IP地址、參與活動次數(shù)贮喧、黑名單用戶的校驗筒狠。比如活動主要針對APP端的用戶校驗,那么根據(jù)參數(shù)其他端的用戶將被攔截箱沦,針對IP辩恼、mac地址、設(shè)備ID和用戶ID可以對用戶參與活動的次數(shù)做校驗谓形,黑名單根據(jù)平時的活動經(jīng)驗攔截掉一部分羊毛黨等異常用戶灶伊。
- 非法請求攔截,做了以上攔截如果還有用戶能繞過限制套耕,那不得不說太牛X了谁帕。比如雙11零點開始還做了答題限制,那么正常人怎么也需要1秒的時間來答題吧冯袍,就算單身30年手速我想也不能超過0.5秒了匈挖,那么針對剛好0點或者在0.5秒以內(nèi)的請求就可以完全攔截掉碾牌。
- 限流,假設(shè)秒殺10000件商品儡循,我們有10臺服務(wù)器舶吗,單機(jī)的QPS在1000,那么理論上1秒就可以搶完择膝,針對微服務(wù)就可以做限流配置誓琼,避免后續(xù)無效的流量打到數(shù)據(jù)庫造成不必要的壓力。針對限流還有另外一種柵欄方式限流肴捉,這是一種純靠運氣的限流方式腹侣,就是在系統(tǒng)約定的請求開始的時間內(nèi)隨機(jī)偏移一段時間,針對每個請求的偏移量不同齿穗,如果在偏移時間之內(nèi)就會被攔截傲隶,反之通過。
性能優(yōu)化
做完無效流量的過濾窃页,那么可能你的無效請求已經(jīng)過濾掉了90%跺株,剩下的有效流量會大大的降低系統(tǒng)的壓力。之后就是需要針對系統(tǒng)的性能做出優(yōu)化了脖卖。
- 頁面靜態(tài)化乒省,參與秒殺活動的商品一般都是已知的,可以針對活動頁面做靜態(tài)化處理畦木,緩存到CDN袖扛。假設(shè)我們一個頁面300K大小,1千萬用戶的流量是多少十籍?這些請求要請求后端服務(wù)器攻锰、數(shù)據(jù)庫,壓力可想而知妓雾,緩存到CDN用戶請求不經(jīng)過服務(wù)器,大大減小了服務(wù)器的壓力垒迂。
- 活動預(yù)熱械姻,針對活動的活動庫存可以獨立出來,不和普通的商品庫存共享服務(wù)机断,活動庫存活動開始前提前加載到redis楷拳,查詢?nèi)孔呔彺妫詈罂蹨p庫存再視情況而定吏奸。
- 獨立部署欢揖,資源充足的情況下可以考慮針對秒殺活動單獨部署一套環(huán)境,這套環(huán)境中可以剝離一些可能無用的邏輯奋蔚,比如不用考慮使用優(yōu)惠券她混、紅包烈钞、下單后贈送積分的一些場景,或者這些場景可以活動結(jié)束后異步的統(tǒng)一發(fā)放坤按。這只是一個舉例毯欣,實際上單獨針對秒殺活動的話你肯定有很多無用的業(yè)務(wù)代碼是可以剝離的,這樣可以提高不少性能臭脓。
經(jīng)過這兩步之后酗钞,最終我們的流量應(yīng)該是呈漏斗狀。
超賣
秒殺除開高并發(fā)高流量下的服務(wù)穩(wěn)定性之外来累,剩下的核心大概就是怎么保證庫存不超賣了砚作,也可以說要保證的是最終一致性。一般來說嘹锁,針對下單和庫存有兩種方式:
下單即扣庫存葫录,這是最常規(guī)的大部分的做法。但是可能在活動中會碰到第二點說到的情況兼耀。
支付完成扣庫存压昼,這種設(shè)計我碰到過就是酒店行業(yè),廉價房放出來之后被黃牛下單搶占庫存導(dǎo)致正常用戶無法下單瘤运,然后黃徘舷迹可以用稍高的價格再售賣給用戶從中牟利,所以會有在一些活動的時候采取支付成功后才占用庫存的做法拯坟。不過這種方式實現(xiàn)起來比較復(fù)雜但金,可能造成大量的無效訂單,在秒殺的場景中不太適用郁季。
針對秒殺建議選擇下單扣庫存的方式冷溃,實現(xiàn)相對簡單而且是常規(guī)做法。
方案
- 首先查詢redis緩存庫存是否充足
- 先扣庫存再落訂單數(shù)據(jù)梦裂,可以防止訂單生成了沒有庫存的超賣問題
- 扣庫存的時候先扣數(shù)據(jù)庫庫存似枕,再扣減redis庫存,保證在同一個事務(wù)里年柠,無論兩者哪一個發(fā)生了異常都會回滾凿歼。有一個問題是可能redis扣成功了由于網(wǎng)絡(luò)問題返回失敗,事務(wù)回滾冗恨,導(dǎo)致數(shù)據(jù)庫和緩存不一致答憔,這樣實際少賣了,可以放到下輪秒殺去掀抹。
這種做法能一定程度上解決問題虐拓,但是也有可能會有其他問題。比如當(dāng)大量請求落在同一條庫存記錄上去做update時傲武,行鎖導(dǎo)致大量的鎖競爭會使得數(shù)據(jù)庫的tps急劇下降蓉驹,性能無法滿足要求城榛。
另外一種做法就是排隊,在服務(wù)層進(jìn)行排隊戒幔,針對同一個商品ID的也就是數(shù)據(jù)庫是一條庫存記錄的做一個內(nèi)存隊列吠谢,串行化去扣減庫存,可以一定程度上緩解數(shù)據(jù)庫的并發(fā)壓力诗茎。
質(zhì)量保障
為了保證系統(tǒng)的穩(wěn)定性工坊,防止你的系統(tǒng)被秒殺,一些質(zhì)量監(jiān)控就不得不做敢订。
- 熔斷限流降級王污,老生常談,根據(jù)壓測情況進(jìn)行限流楚午,可以使用sentinel或者h(yuǎn)ystrix昭齐。另外前端后端都該有降級開關(guān)。
- 監(jiān)控矾柜,該上的都上阱驾,QPS監(jiān)控、容器監(jiān)控怪蔑、CPU里覆、緩存、IO監(jiān)控等等缆瓣。
- 演練喧枷,大型秒殺事前演練少不了,不能冒冒失失的就上了吧弓坞。
- 核對隧甚、預(yù)案,事后庫存訂單 金額渡冻、數(shù)量核對戚扳,是否發(fā)生超賣了?金額是否正常?都是必須的族吻。預(yù)案可以在緊急情況下進(jìn)行降級咖城。
數(shù)據(jù)統(tǒng)計
活動做完了,數(shù)據(jù)該怎么統(tǒng)計呼奢?
- 前端埋點
- 數(shù)據(jù)大盤,通過后臺服務(wù)的打點配合監(jiān)控系統(tǒng)可以通過大盤直觀的看到一些活動的監(jiān)控和數(shù)據(jù)
- 離線數(shù)據(jù)分析切平,事后活動的數(shù)據(jù)可以同步到離線數(shù)倉做進(jìn)一步的分析統(tǒng)計
總結(jié)
總的來說握础,面對巨量的流量我們的方式就是首先通過各種條件先篩選掉無效流量,進(jìn)行流量錯峰悴品,然后再對現(xiàn)有的系統(tǒng)性能做出優(yōu)化禀综,比如頁面靜態(tài)化简烘,庫存商品預(yù)熱,也可以通過獨立部署的方式和其他的環(huán)境做隔離定枷,最后還要解決高并發(fā)下緩存一致性孤澎、庫存不能超賣的問題,防止大量的并發(fā)打爆你的數(shù)據(jù)庫欠窒。
一個完整的活動從前端到后端是一個完整的鏈路覆旭,中間有事前的演練工作,事后的數(shù)據(jù)分析等都是必不可少的環(huán)節(jié)岖妄。