什么是秒殺?
“秒殺”是商家在特定時(shí)間點(diǎn)進(jìn)行促銷的一種運(yùn)營手段,體現(xiàn)在系統(tǒng)層面,是指一個(gè)Web系統(tǒng),在一秒鐘收到數(shù)以萬計(jì)的用戶請(qǐng)求,來搶購數(shù)量有限的促銷產(chǎn)品。本質(zhì)上,秒殺系統(tǒng)就是一個(gè)“三高”系統(tǒng),即高并發(fā)、高性能、高可用的分布式系統(tǒng)。本文主要從“高并發(fā)”的角度,來看看需要解決的主要問題。
秒殺需要處理并發(fā)讀,并發(fā)寫。“并發(fā)讀”需要通過各種手段減少用戶到服務(wù)端來讀數(shù)據(jù),而“并發(fā)寫”主要需要保證數(shù)據(jù)的正確性,確保大流量下數(shù)據(jù)一致。
秒殺系統(tǒng)關(guān)注三方面
下面以一個(gè)秒殺系統(tǒng)的例子,來看看一個(gè)秒殺系統(tǒng)主要關(guān)注的三方面問題。
一、高并發(fā)訪問
秒殺前用戶不斷刷新頁面查看,來獲得購買按鈕,搶購時(shí)集中點(diǎn)擊并發(fā)購買,核心是對(duì)系統(tǒng)數(shù)據(jù)的高并發(fā)讀訪問,可以通過如下手段進(jìn)行防護(hù)。
訪問攔截:從瀏覽器/反向代理/Web層/服務(wù)層/數(shù)據(jù)層多層攔截流量,盡量把訪問攔截在離用戶更近的層,減少對(duì)后臺(tái)的壓力;
分流:主要通過分布式集群技術(shù),多臺(tái)機(jī)器處理,提高并發(fā)能力;
動(dòng)靜分離:將動(dòng)態(tài)數(shù)據(jù)和靜態(tài)數(shù)據(jù)盡量分離,并通過緩存和CDN等技術(shù)對(duì)數(shù)據(jù)讀取進(jìn)行加速;
二、數(shù)據(jù)正確性
秒殺伴隨這一系列業(yè)務(wù)流程,包括瀏覽商品、進(jìn)入搶購頁、購買、扣減庫存、支付。其中并發(fā)寫主要與扣減庫存有關(guān),要保證數(shù)據(jù)的正確性,防止超賣發(fā)生。大概的解決方法有:
減庫存:事務(wù)判斷,熱點(diǎn)商品放到單獨(dú)的熱點(diǎn)庫,增加并發(fā)鎖
熱點(diǎn):對(duì)核心熱點(diǎn)數(shù)據(jù)進(jìn)行實(shí)時(shí)分析,并在系統(tǒng)中進(jìn)行優(yōu)化或隔離
異步化:是指把購買請(qǐng)求的接受和處理異步化。購買請(qǐng)求先放到隊(duì)列中,這個(gè)過程非常高效,返回客戶信息。
限流降級(jí):請(qǐng)求限流,控制系統(tǒng)的訪問;對(duì)服務(wù)的下游依賴進(jìn)行降級(jí),保證核心鏈路
三、防作弊
搶購還需要保證公平性,方舟如購票插件購買火車票、黃牛黨等。在技術(shù)角度,大概方法有:
答題:為了增加購買的復(fù)雜度;延緩請(qǐng)求。
Cache校驗(yàn):處理用戶購買請(qǐng)求時(shí)校驗(yàn)緩存中是否已記錄此商品的購買。
秒殺系統(tǒng)設(shè)計(jì)原則
1、前臺(tái)請(qǐng)求數(shù)盡量少
前臺(tái)請(qǐng)求會(huì)增加瀏覽器的負(fù)擔(dān),盡量簡(jiǎn)化或合并頁面大小(css/JS,圖片等),去掉頁面裝飾;減少DNCS解析
2、后臺(tái)數(shù)據(jù)盡量少
后臺(tái)數(shù)據(jù)會(huì)增加網(wǎng)絡(luò)傳輸,壓縮和字符編碼,消耗CPU。需要梳理后臺(tái)依賴的數(shù)據(jù)和服務(wù),關(guān)注序列化/反序列化方式,減少不必要的數(shù)據(jù)交互
3、調(diào)用路徑要盡量短
分布式調(diào)用錯(cuò)綜復(fù)雜,需要盡量縮短調(diào)用鏈路;減少第三方依賴,盡量弱依賴,并做好應(yīng)用分級(jí)
4、盡量不要有單點(diǎn)
高可用和穩(wěn)定性角度,消除單點(diǎn);服務(wù)無狀態(tài)化,解藕服務(wù)狀態(tài)和機(jī)器,如機(jī)器配置動(dòng)態(tài)化;有狀態(tài)的存儲(chǔ),通過冗余備份提高可用性。
高并發(fā)之“訪問攔截”
高并發(fā)的訪問攔截主要思路是:
從瀏覽器/反向代理/Web層/服務(wù)層/數(shù)據(jù)層多層攔截流量,盡量把訪問攔截在離用戶更近的層,盡可能地過濾掉無效請(qǐng)求,讓“漏斗”最末端的才是有效請(qǐng)求。同時(shí),用戶請(qǐng)求更早的得到處理,也減少了每次請(qǐng)求的RT,進(jìn)而提高了系統(tǒng)的QPS和并發(fā)程度。
瀏覽器訪問攔截
防止用戶的不必要的點(diǎn)擊,可以在產(chǎn)品層面限制用戶的行為,比如點(diǎn)擊后按鈕不可用,限定時(shí)間接受請(qǐng)求。當(dāng)然,為了防止惡意搶購,還需要在防作弊上做其他嘗試,比如前文講的答題/cache校驗(yàn)等。
CDN加速
CDN的全稱是Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。簡(jiǎn)單來講,就是把原服務(wù)器上數(shù)據(jù)復(fù)制到其他服務(wù)器上,用戶就近訪問服務(wù)器獲取資源。缺點(diǎn)是內(nèi)容變更生效慢。
反向代理訪問攔截
主要是動(dòng)靜分離,靜態(tài)文件作為web項(xiàng)目的一部分進(jìn)行發(fā)布;另外通過Nginx托管靜態(tài)文件 ,減輕Web服務(wù)的壓力。
Web層和Service層訪問攔截
主要是動(dòng)態(tài)數(shù)據(jù)訪問,可以采用緩存的策略,減少對(duì)下一層的數(shù)據(jù)訪問。緩存又可分為本地緩存和redis、memcache等緩存中間件。
高并發(fā)之“分流”
分流主要通過分布式集群技術(shù),多臺(tái)機(jī)器處理,提高并發(fā)能力。
DNS輪詢:從App/Browser入口,對(duì)請(qǐng)求進(jìn)行分類,靜態(tài)數(shù)據(jù)直接去CDN獲取,同時(shí)Nginx進(jìn)行集群化,通過DNCS輪詢。
Nginx負(fù)載均衡:Nginx可以支持10W的并發(fā)訪問, Nginx支持配置請(qǐng)求的代理策略,把請(qǐng)求路由到多個(gè)Web服務(wù)器處理。Nginx支持的負(fù)載均衡策略包括:輪詢,權(quán)重,ip_hash,fair,url_hash等
分布式架構(gòu)負(fù)載策略:Web層調(diào)用service,以及service之間的調(diào)用,每個(gè)service都需要部署多份。目前最常用的兩個(gè)框架技術(shù),spring cloud、dubbo、HSF,都采用客戶端負(fù)載均衡策略,路由到service的不同實(shí)例。
Redis集群:redis單臺(tái)幾十萬的QPS,可通過Redis集群,把數(shù)據(jù)分配到多臺(tái)服務(wù)器上,減輕每臺(tái)機(jī)器的負(fù)載。
MySQL讀寫分離:一般不對(duì)寫請(qǐng)求分流,也可通過分布式數(shù)據(jù)庫技術(shù)對(duì)寫請(qǐng)求分流。對(duì)于讀請(qǐng)求,一般采用讀寫分離的策略。讀庫應(yīng)用單獨(dú)設(shè)置索引,提高讀性能。
高并發(fā)之“動(dòng)靜分離”
動(dòng)態(tài)頁面:根據(jù)實(shí)時(shí)數(shù)據(jù)渲染的頁面,需要調(diào)用后臺(tái),讀取速度慢
靜態(tài)頁面:存儲(chǔ)在文件系統(tǒng)的文件,不經(jīng)常變動(dòng),讀取速度很
為了提升效率,盡可能靜態(tài)化,用靜態(tài)頁面,替換動(dòng)態(tài)頁面。例如,商品信息頁,商品信息在發(fā)布后,保存下商品信息的靜態(tài)頁面;僅訪問必要的數(shù)據(jù)實(shí)時(shí)數(shù)據(jù)。
幾個(gè)關(guān)注點(diǎn):
1、 靜態(tài)數(shù)據(jù)緩存到離用戶最近的地方。比如瀏覽器、CDN、服務(wù)端的 Cache
2、 可以直接緩存 HTTP 連接。關(guān)鍵是數(shù)據(jù)中是否含有和訪問者相關(guān)的個(gè)性化數(shù)據(jù),比如輸出的數(shù)據(jù)是否和 URL、瀏覽者、時(shí)間、地域相關(guān),以及是否含有 Cookie 等私密數(shù)據(jù)。
3、 經(jīng)常使用Nginx和Web服務(wù)器緩存靜態(tài)文件。JAVA 系統(tǒng)本身不擅長(zhǎng)處理大量連接請(qǐng)求,需要在Nginx和Web側(cè)盡量緩存
4、 動(dòng)態(tài)數(shù)據(jù)盡量Json化。減少不必要的數(shù)據(jù)傳輸和解析
5、 緩存還需要考慮失效問題/命中率問題/發(fā)布更新問題/動(dòng)靜數(shù)據(jù)合并;以失效為例,一般有三種方式:超時(shí)更新,定時(shí)更新,通知更新。
秒殺之“減庫存”
減庫存一般有如下方式:下單減庫存,付款減庫存,預(yù)扣庫存。
其中,筆者比較推薦“下單減庫存”,因?yàn)閰⒓用霘ⅲI到就是賺到,成功下單后卻不付款的情況比較少;同時(shí)賣家對(duì)秒殺商品的庫存有嚴(yán)格限制,邏輯上更為簡(jiǎn)單。
當(dāng)然“下單減庫存”,要保證庫存數(shù)據(jù)不能為負(fù)數(shù)。
一些方法:
1、事務(wù)判斷,即保證減后庫存不能為負(fù)數(shù),否則就回滾;
2、直接設(shè)置數(shù)據(jù)庫的字段數(shù)據(jù)為無符號(hào)整數(shù),小于零時(shí)會(huì)直接報(bào)錯(cuò);
3、熱點(diǎn)商品放在單獨(dú)的熱點(diǎn)庫
4、并發(fā)鎖:增加并發(fā)鎖,比如排隊(duì)等
5、計(jì)數(shù)器:在Web層或Service層做一個(gè)計(jì)數(shù)器,技術(shù)到達(dá)閾值,直接返回?fù)屬徥 ?/p>
6、按商品路由:把對(duì)同一品類商品的搶購路由到同一個(gè)service以及DB鏈接上,減少不必要的鎖競(jìng)爭(zhēng)。
實(shí)踐中的一些原則:
1. 將讀數(shù)據(jù)緩存在 Web 端,過濾掉無效的數(shù)據(jù)讀;
2. 對(duì)讀數(shù)據(jù)不做強(qiáng)一致性校驗(yàn);
3. 對(duì)寫數(shù)據(jù)做基于時(shí)間的分片,過濾過期請(qǐng)求;
4. 對(duì)寫請(qǐng)求做限流保護(hù),過濾超載請(qǐng)求;
5. 對(duì)寫數(shù)據(jù)強(qiáng)一致性校驗(yàn),僅校驗(yàn)最后有效的數(shù)據(jù)。
秒殺之“熱點(diǎn)”
秒殺需要對(duì)核心熱點(diǎn)數(shù)據(jù)進(jìn)行實(shí)時(shí)分析,處理思路可以從業(yè)務(wù)/系統(tǒng)/數(shù)據(jù)不同角度進(jìn)行優(yōu)化/限制/隔離,比如:
靜態(tài)熱點(diǎn)數(shù)據(jù):提前預(yù)測(cè)。賣家報(bào)名,提前打標(biāo)
動(dòng)態(tài)熱點(diǎn)數(shù)據(jù):系統(tǒng)在運(yùn)行過程中臨時(shí)產(chǎn)生的熱點(diǎn)
也可以增加單獨(dú)的熱點(diǎn)分析組件,總體控制系統(tǒng)業(yè)務(wù)的熱點(diǎn),并做對(duì)應(yīng)的限制和隔離方法。
秒殺之“異步”
秒殺系統(tǒng)設(shè)計(jì)上,經(jīng)常使用消息隊(duì)列來緩沖瞬時(shí)流量,把同步的請(qǐng)求調(diào)用轉(zhuǎn)換成異步的間接推送,中間通過一個(gè)隊(duì)列在一端承接瞬時(shí)的流量洪峰,在另一端平滑地消費(fèi)消息。
本質(zhì)上,就是把一步變成兩步,其中增加一些機(jī)制來緩沖。比如在秒殺優(yōu)惠券搶購,用戶搶碼(確定用戶是否有資格) 和 系統(tǒng)發(fā)碼(為有資格用戶分發(fā)優(yōu)惠券) ,搶碼QPS可能10幾萬,只需要知道是否有資格;而發(fā)碼僅針對(duì)有資格用戶,QPS可能僅僅幾千,使用消息隊(duì)列,可以大大提高效率。
除了消息隊(duì)列,也可以使用其他方式排隊(duì),比如線程池加鎖等待、內(nèi)存排隊(duì)算法(如FIFO)、文件排隊(duì)(如基于 MySQL binlog 的同步機(jī)制)
Takeaways
- 秒殺系統(tǒng)就是一個(gè)“三高”系統(tǒng),即高并發(fā)、高性能和高可用的分布式系統(tǒng)
- 秒殺設(shè)計(jì)原則:前臺(tái)請(qǐng)求盡量少,后臺(tái)數(shù)據(jù)盡量少,調(diào)用鏈路盡量短,盡量不要有單點(diǎn)
- 秒殺高并發(fā)方法:訪問攔截、分流、動(dòng)靜分離
- 秒殺數(shù)據(jù)方法:減庫存策略、熱點(diǎn)、異步、限流降級(jí)
- 訪問攔截主要思路:通過CDN和緩存技術(shù),盡量把訪問攔截在離用戶更近的層,盡可能地過濾掉無效請(qǐng)求。
- 分流主要思路:通過分布式集群技術(shù),多臺(tái)機(jī)器處理,提高并發(fā)能力。
最后
秒殺要求對(duì)系統(tǒng)的高并發(fā)、高性能和高可用都有極高的要求,本文主要從高并發(fā)的角度進(jìn)行了闡述。對(duì)于高性能和高可用,感興趣的同學(xué)可以關(guān)注小編