什么是秒殺?
“秒殺”是商家在特定時間點進行促銷的一種運營手段,體現(xiàn)在系統(tǒng)層面,是指一個Web系統(tǒng),在一秒鐘收到數(shù)以萬計的用戶請求,來搶購數(shù)量有限的促銷產品。本質上,秒殺系統(tǒng)就是一個“三高”系統(tǒng),即高并發(fā)、高性能、高可用的分布式系統(tǒng)。本文主要從“高并發(fā)”的角度,來看看需要解決的主要問題。
秒殺需要處理并發(fā)讀,并發(fā)寫。“并發(fā)讀”需要通過各種手段減少用戶到服務端來讀數(shù)據(jù),而“并發(fā)寫”主要需要保證數(shù)據(jù)的正確性,確保大流量下數(shù)據(jù)一致。
秒殺系統(tǒng)關注三方面
下面以一個秒殺系統(tǒng)的例子,來看看一個秒殺系統(tǒng)主要關注的三方面問題。

一、高并發(fā)訪問
秒殺前用戶不斷刷新頁面查看,來獲得購買按鈕,搶購時集中點擊并發(fā)購買,核心是對系統(tǒng)數(shù)據(jù)的高并發(fā)讀訪問,可以通過如下手段進行防護。
訪問攔截:從瀏覽器/反向代理/Web層/服務層/數(shù)據(jù)層多層攔截流量,盡量把訪問攔截在離用戶更近的層,減少對后臺的壓力;
分流:主要通過分布式集群技術,多臺機器處理,提高并發(fā)能力;
動靜分離:將動態(tài)數(shù)據(jù)和靜態(tài)數(shù)據(jù)盡量分離,并通過緩存和CDN等技術對數(shù)據(jù)讀取進行加速;
二、數(shù)據(jù)正確性
秒殺伴隨這一系列業(yè)務流程,包括瀏覽商品、進入搶購頁、購買、扣減庫存、支付。其中并發(fā)寫主要與扣減庫存有關,要保證數(shù)據(jù)的正確性,防止超賣發(fā)生。大概的解決方法有:
減庫存:事務判斷,熱點商品放到單獨的熱點庫,增加并發(fā)鎖
熱點:對核心熱點數(shù)據(jù)進行實時分析,并在系統(tǒng)中進行優(yōu)化或隔離
異步化:是指把購買請求的接受和處理異步化。購買請求先放到隊列中,這個過程非常高效,返回客戶信息。
限流降級:請求限流,控制系統(tǒng)的訪問;對服務的下游依賴進行降級,保證核心鏈路
三、防作弊
搶購還需要保證公平性,方舟如購票插件購買火車票、黃牛黨等。在技術角度,大概方法有:
答題:為了增加購買的復雜度;延緩請求。
Cache校驗:處理用戶購買請求時校驗緩存中是否已記錄此商品的購買。
秒殺系統(tǒng)設計原則
1、前臺請求數(shù)盡量少
前臺請求會增加瀏覽器的負擔,盡量簡化或合并頁面大小(css/JS,圖片等),去掉頁面裝飾;減少DNCS解析
2、后臺數(shù)據(jù)盡量少
后臺數(shù)據(jù)會增加網(wǎng)絡傳輸,壓縮和字符編碼,消耗CPU。需要梳理后臺依賴的數(shù)據(jù)和服務,關注序列化/反序列化方式,減少不必要的數(shù)據(jù)交互
3、調用路徑要盡量短
分布式調用錯綜復雜,需要盡量縮短調用鏈路;減少第三方依賴,盡量弱依賴,并做好應用分級
4、盡量不要有單點
高可用和穩(wěn)定性角度,消除單點;服務無狀態(tài)化,解藕服務狀態(tài)和機器,如機器配置動態(tài)化;有狀態(tài)的存儲,通過冗余備份提高可用性。
高并發(fā)之“訪問攔截”
高并發(fā)的訪問攔截主要思路是:
從瀏覽器/反向代理/Web層/服務層/數(shù)據(jù)層多層攔截流量,盡量把訪問攔截在離用戶更近的層,盡可能地過濾掉無效請求,讓“漏斗”最末端的才是有效請求。同時,用戶請求更早的得到處理,也減少了每次請求的RT,進而提高了系統(tǒng)的QPS和并發(fā)程度。

瀏覽器訪問攔截
防止用戶的不必要的點擊,可以在產品層面限制用戶的行為,比如點擊后按鈕不可用,限定時間接受請求。當然,為了防止惡意搶購,還需要在防作弊上做其他嘗試,比如前文講的答題/cache校驗等。
CDN加速
CDN的全稱是Content Delivery Network,即內容分發(fā)網(wǎng)絡。簡單來講,就是把原服務器上數(shù)據(jù)復制到其他服務器上,用戶就近訪問服務器獲取資源。缺點是內容變更生效慢。
反向代理訪問攔截
主要是動靜分離,靜態(tài)文件作為web項目的一部分進行發(fā)布;另外通過Nginx托管靜態(tài)文件 ,減輕Web服務的壓力。
Web層和Service層訪問攔截
主要是動態(tài)數(shù)據(jù)訪問,可以采用緩存的策略,減少對下一層的數(shù)據(jù)訪問。緩存又可分為本地緩存和redis、memcache等緩存中間件。
高并發(fā)之“分流”
分流主要通過分布式集群技術,多臺機器處理,提高并發(fā)能力。

DNS輪詢:從App/Browser入口,對請求進行分類,靜態(tài)數(shù)據(jù)直接去CDN獲取,同時Nginx進行集群化,通過DNCS輪詢。
Nginx負載均衡:Nginx可以支持10W的并發(fā)訪問, Nginx支持配置請求的代理策略,把請求路由到多個Web服務器處理。Nginx支持的負載均衡策略包括:輪詢,權重,ip_hash,fair,url_hash等
分布式架構負載策略:Web層調用service,以及service之間的調用,每個service都需要部署多份。目前最常用的兩個框架技術,spring cloud、dubbo、HSF,都采用客戶端負載均衡策略,路由到service的不同實例。
Redis集群:redis單臺幾十萬的QPS,可通過Redis集群,把數(shù)據(jù)分配到多臺服務器上,減輕每臺機器的負載。
MySQL讀寫分離:一般不對寫請求分流,也可通過分布式數(shù)據(jù)庫技術對寫請求分流。對于讀請求,一般采用讀寫分離的策略。讀庫應用單獨設置索引,提高讀性能。
高并發(fā)之“動靜分離”
動態(tài)頁面:根據(jù)實時數(shù)據(jù)渲染的頁面,需要調用后臺,讀取速度慢
靜態(tài)頁面:存儲在文件系統(tǒng)的文件,不經常變動,讀取速度很
為了提升效率,盡可能靜態(tài)化,用靜態(tài)頁面,替換動態(tài)頁面。例如,商品信息頁,商品信息在發(fā)布后,保存下商品信息的靜態(tài)頁面;僅訪問必要的數(shù)據(jù)實時數(shù)據(jù)。
幾個關注點:
1、 靜態(tài)數(shù)據(jù)緩存到離用戶最近的地方。比如瀏覽器、CDN、服務端的 Cache
2、 可以直接緩存 HTTP 連接。關鍵是數(shù)據(jù)中是否含有和訪問者相關的個性化數(shù)據(jù),比如輸出的數(shù)據(jù)是否和 URL、瀏覽者、時間、地域相關,以及是否含有 Cookie 等私密數(shù)據(jù)。
3、 經常使用Nginx和Web服務器緩存靜態(tài)文件。JAVA 系統(tǒng)本身不擅長處理大量連接請求,需要在Nginx和Web側盡量緩存
4、 動態(tài)數(shù)據(jù)盡量Json化。減少不必要的數(shù)據(jù)傳輸和解析
5、 緩存還需要考慮失效問題/命中率問題/發(fā)布更新問題/動靜數(shù)據(jù)合并;以失效為例,一般有三種方式:超時更新,定時更新,通知更新。
秒殺之“減庫存”
減庫存一般有如下方式:下單減庫存,付款減庫存,預扣庫存。
其中,筆者比較推薦“下單減庫存”,因為參加秒殺,買到就是賺到,成功下單后卻不付款的情況比較少;同時賣家對秒殺商品的庫存有嚴格限制,邏輯上更為簡單。
當然“下單減庫存”,要保證庫存數(shù)據(jù)不能為負數(shù)。
一些方法:
1、事務判斷,即保證減后庫存不能為負數(shù),否則就回滾;
2、直接設置數(shù)據(jù)庫的字段數(shù)據(jù)為無符號整數(shù),小于零時會直接報錯;
3、熱點商品放在單獨的熱點庫
4、并發(fā)鎖:增加并發(fā)鎖,比如排隊等
5、計數(shù)器:在Web層或Service層做一個計數(shù)器,技術到達閾值,直接返回搶購失敗。
6、按商品路由:把對同一品類商品的搶購路由到同一個service以及DB鏈接上,減少不必要的鎖競爭。
實踐中的一些原則:
1. 將讀數(shù)據(jù)緩存在 Web 端,過濾掉無效的數(shù)據(jù)讀;
2. 對讀數(shù)據(jù)不做強一致性校驗;
3. 對寫數(shù)據(jù)做基于時間的分片,過濾過期請求;
4. 對寫請求做限流保護,過濾超載請求;
5. 對寫數(shù)據(jù)強一致性校驗,僅校驗最后有效的數(shù)據(jù)。
秒殺之“熱點”
秒殺需要對核心熱點數(shù)據(jù)進行實時分析,處理思路可以從業(yè)務/系統(tǒng)/數(shù)據(jù)不同角度進行優(yōu)化/限制/隔離,比如:
靜態(tài)熱點數(shù)據(jù):提前預測。賣家報名,提前打標
動態(tài)熱點數(shù)據(jù):系統(tǒng)在運行過程中臨時產生的熱點
也可以增加單獨的熱點分析組件,總體控制系統(tǒng)業(yè)務的熱點,并做對應的限制和隔離方法。
秒殺之“異步”
秒殺系統(tǒng)設計上,經常使用消息隊列來緩沖瞬時流量,把同步的請求調用轉換成異步的間接推送,中間通過一個隊列在一端承接瞬時的流量洪峰,在另一端平滑地消費消息。

本質上,就是把一步變成兩步,其中增加一些機制來緩沖。比如在秒殺優(yōu)惠券搶購,用戶搶碼(確定用戶是否有資格) 和 系統(tǒng)發(fā)碼(為有資格用戶分發(fā)優(yōu)惠券) ,搶碼QPS可能10幾萬,只需要知道是否有資格;而發(fā)碼僅針對有資格用戶,QPS可能僅僅幾千,使用消息隊列,可以大大提高效率。
除了消息隊列,也可以使用其他方式排隊,比如線程池加鎖等待、內存排隊算法(如FIFO)、文件排隊(如基于 MySQL binlog 的同步機制)
Takeaways
- 秒殺系統(tǒng)就是一個“三高”系統(tǒng),即高并發(fā)、高性能和高可用的分布式系統(tǒng)
- 秒殺設計原則:前臺請求盡量少,后臺數(shù)據(jù)盡量少,調用鏈路盡量短,盡量不要有單點
- 秒殺高并發(fā)方法:訪問攔截、分流、動靜分離
- 秒殺數(shù)據(jù)方法:減庫存策略、熱點、異步、限流降級
- 訪問攔截主要思路:通過CDN和緩存技術,盡量把訪問攔截在離用戶更近的層,盡可能地過濾掉無效請求。
- 分流主要思路:通過分布式集群技術,多臺機器處理,提高并發(fā)能力。
最后
秒殺要求對系統(tǒng)的高并發(fā)、高性能和高可用都有極高的要求,本文主要從高并發(fā)的角度進行了闡述。對于高性能和高可用,感興趣的同學可以關注小編