提到秒殺, 很多人都會覺得這是一件技術要求很高的事情, 因為這涉及到超大通路量(可能瞬間千萬倍的使用者通路商品)、維護資料一緻性(不能超賣), 前者對性能有極高的要求, 而後者又正好拉低了性能,本文談談秒殺的設計思路, 并在最後給出秒殺設計的簡單模型圖。
秒殺的情景
生活中有很多秒殺的情景, 例如商家促銷, 像一進制搶茅台, 五毛錢搶寶馬(這兒隻是一個例子), 假如一百萬人來搶十瓶茅台, 這時候肯定不能多賣出, 也就是不能被大于10的人數搶到, 通常最後時間會有一個倒計時按鈕, 30...29...28......3...2...1 GO! 之後躍躍欲試的人們開始搶。
這時候有以下問題需要被考慮 :
第一是多個用戶端的時間如何保持同步, 也就是讓大家看到時間是一緻的, 不能你顯示3, 而我這還顯示 30 。
第二是如何保證有沒有黃牛用機器人搶 。
第三是如何確定後端伺服器可以支撐住這巨大的流量。
......
秒殺解決思路
有了上面的情景以及引出來的問題, 來看看秒殺方案的設計思路, 我們伺服器如何應對這一百萬的TPS呢?
首先想到的是擴容, 但這是不現實的, 因為擴容需要很多很多機器, TPS增加一萬倍對實體伺服器的性能要求遠遠不止一萬倍, 另外對于一個商家來說, 為了這一次促銷活動購置伺服器是不劃算的, 平時勢必有衆多的機器處于閑置狀态。
沒法擴容, 那麼也就意味着要使用其他方法, 如果所有請求通路一台實體機器肯定不行, 一百萬的資料通路無論如何分庫分表都無濟于事, 因為面對的每一條都是熱點資料, 是以要用到分布式架構的思路。
秒殺的技術方案
分布式, 可以降低伺服器的壓力, 下面開始講述秒殺的設計思路。
方案一
很明顯, 要讓一百萬使用者能夠同時打開搶貨的網頁, 勢必要用要到CDN(内容分發網絡, CDN主要作用有兩個, 一方面是将一些不會改變的靜态資源放到離用戶端較近的邊緣伺服器上, 這樣用戶端請求資料的時候可以直接從邊緣伺服器擷取, 降低中心伺服器的壓力, 另外一方面可以把小服務部署到 CDN 結點上去,這樣,目前端頁面來問開沒開始時,這個小服務除了告訴前端開沒開始外,它還可以統計下有多少人線上。每個小服務會把目前線上等待秒殺的人數每隔一段時間就回傳給我們的資料中心,于是我們就知道全網總共線上的人數有多少。

假設,我們知道有大約 100 萬的人線上等着搶,那麼,在我們快要開始的時候,由資料中心向各個部署在 CDN 結點上的小服務上傳遞一個機率值,這個機率值為CDN節點人數權重乘以獲獎機率, 比如說是𝑒e。于是,當秒殺開始的時候,這 100 萬使用者都在點下單按鈕,首先他們請求到的是 CDN 上的這些服務,這些小服務按照 𝑒e 的量把使用者放到後面的資料中心,也就是放過去 人數∗𝑒人數∗e,剩下的都直接傳回秒殺已結束。
方案二
利用我們分布式中限流、網關等知識, 将請求層層篩選, 降低最後連接配接到資料庫的請求。
首先也是利用CDN将靜态資源分發在邊緣伺服器上, 當進行服務請求時, 先進行鑒權, 鑒權主要是篩選機器人等非人工搶購, 根據實際經驗, 鑒權可以篩選很大一部分使用者, 例如是否登入。
當鑒權确定是真實有效的使用者之後,也就是LVS+Keepalived 将請求配置設定到不同的Nginx上,一般會建立Nginx叢集, 然後再通過網關叢集, 即使這樣還是要增加一些限流措施, 如果到這一步還是有很多請求壓到資料庫勢必撐不住, 那麼可以采取服務限流、服務降級等措施,進行削峰處理。
到這兒理論上流量就不高了, 如果還是很高, 後面就将熱點資料放進緩存叢集中進行預熱, 同時設定定時任務,一方面關注資料庫與緩存的一緻性, 另一方面關閉逾時未支付的訂單, 當訂單送出之後 交給任務隊列, 生成訂單、修改資料庫、做好持久化工作。
架構圖:
這就是整個“秒殺”的技術細節,是不是有點不敢相信?
與這種秒殺業務類似的還有12306搶票, 這個也是瞬間高流量, 但是上面提到的架構就不适合了,因為12306完全不知道使用者來是要買哪張火車票的。不知道這個資訊,很不好過濾使用者,而且使用者在買票前需要有很多查詢操作,然後在查詢中選擇自己的車票。也就意味着沒法在開始就過濾使用者。
12306 最好的應對方式,除了不要一次把所有的票放出來,而是分批在不同的時間段把票放出來,這樣可以讓人們不要集中在一個時間點來搶票,做到人肉分流,可以降低一些并發度。
另外,12306 最好是用預售的方式,讓大家把自己的購票先輸入到系統中。系統并不真正放票,而是把大家的需求都收集好,然後做整體統籌安排,該增加車次的增加車次,該加車廂的加車廂,這樣可以確定大家都能走。實在不行,就抽簽了。
總結
我們可以看到,解決秒殺這種特定業務場景,可以使用 CDN 的邊緣結點來扛流量,然後過濾使用者請求(限流使用者請求),來保護資料中心的系統,這樣才讓整個秒殺得以順利進行。也可以像方案二那樣逐層過濾請求, 這種業務場景和雙十一相同嗎? 如果像雙 11 那樣,想盡可能多地賣出商品, 那麼就不像秒殺了。這是要盡可能多地收訂單,但又不能超過庫存,其中還有大量的銀行支付,各大倉庫的庫存查詢和配置設定,這些都是非常慢的操作。為了保證一緻性,還要能夠扛得住像雙 11 這樣的大規模并發通路,那麼,應該怎麼做呢?
使用秒殺這樣的解決方案基本上不太科學了。這個時候就需要認認真真地做高并發的架構和測試了,需要各個系統把自己的性能調整上去,還要小心地做性能規劃,更要把分布式的彈力設計做好,最後是要不停地做性能測試,找到整個架構的系統瓶頸,然後不斷地做水準擴充,以解決大規模的并發。
有些時候,我們總是在想資料中心的解決方案。其實,我們有時候也需要換一換思路,也許,在資料中心解決并不一定是最好的方式,放在邊緣來解決可能會更好一些。尤其是針對一些有地域特征的業務,比如像外賣、共享單車、打車這樣的業務。其實,把一些簡單的業務邏輯放在邊緣,比放在資料中心不但能夠有更好的性能,還有更便宜的成本。我覺得,随着請求量越來越大,資料也越來越多,資料中心是有點到瓶頸了,而需要邊緣結 點來幫忙了。而且,這個邊緣化解決方案的趨勢也會越來越有優勢。