什么是“秒杀”?为什么传统项目中也有“秒杀”的概念?一起来分析一下.
什么是“秒殺”?為什么傳統項目中也有“秒殺”的概念?一起來分析一下.
?
如題所述,到底什么是“秒殺”,為什么我不是做電商的,還和秒殺扯上關系了?
或者說“秒殺”一定是電商項目的一個關鍵字嗎?
?
當然,筆者這樣反問了,那么當然,秒殺就不是特定電商項目的事情了。可以將其理解為這一類的業務的一個代稱。
哪一類業務呢?高并發項目。
?
?
舉個行業的例子來說;(這里還是以電商項目為例,因為電商項目的并發場景比較便于理解)
雙十一,是每個人都熱血沸騰的時刻,為什么這里說是時刻呢?因為從我們技術人眼中,雙十一不是一天的概念,而是那么一瞬間的概念,通常來說,我們比較關心雙十一 0點的時候的一個流量情況,而不是關心一天的流量情況(當然,一天也得關心,此處相對而論);
從產品角度、用戶角度而言,雙十一是一個活動,一個通過各種紅包、優惠價等方式形成的一種促銷活動,這種活動,將會吸引用戶大量購物,產生大量交易訂單,從而帶動經濟流動,產生各種經濟利益。
從技術角度而言,雙十一產生的數據量也是龐大的,此處通過去年雙十一一個圖片表達。
這是歷年的雙十一當天的訂單數量產生量,以2019年為例,一天的時間產生了2684億元的成交量
,這當天的并發量是怎么評估的。
?
再看另一張圖,這里統計的是突破1000億耗時,用了64分鐘成交金額1000億,這并發量又是多少?
.
2秒不到產生了100億的交易額
?
這種數據量的產生的流量高峰是什么樣的高度,相信這都是行業能標桿的表現。
這里的并發量是真的不敢想象,我們簡單的計算一下,1.36秒,產生了100億的交易額。
假設1個訂單平均是100塊錢,那么1.36秒就是產生了 1億的訂單數
假設1個訂單平均是1000塊錢,那么1.36秒就是產生了 10000000的訂單數
那么就可以認為 并發為 7,352,941/s
一秒 700多萬的并發,試問這什么數據庫,什么服務能承受得了?
?
此處再強調,700多萬的并發量是站在有效訂單生成的基礎上計算而來,并且是估算1000元一個訂單的平均成交額。
要知道,我們平時的搶購經驗來說,基本上是一件商品,上千上萬的用戶在搶購,這個 700多萬 應當 * 1000 都不為過。
?
那么給各位先分析了一個數據上的概念,我們對后面的分析可能會更加理解一些。
那么在這種情況,上萬人搶購一件商品的庫存,對于系統而言,如何承擔這樣大的壓力,并且有多少這種搶購存在,對系統的負載又是一大考驗。
從數據庫層面,我們需要生成訂單,減庫存,這么大的并發請求,我們如何能保證一件商品,最終成交只會有一個訂單,其他請求都搶購失敗。我們如何保證我們能夠保證系統能夠正常處理這么大的并發搶購量,而不是導致此次搶購活動癱瘓,導致就連一個訂單都沒有生成?這些都是我們此次需要思考的問題。
對于數據而言,我們需要將庫存 -1,然后生成一個訂單數;這樣才是正確的流程,并且庫存只能是>=0,切不能成為負數。我們系統如何控制此種規則嚴格執行?在很多場景,很多顧客在搶購成功后,生成訂單了,然后不想要了,就會取消訂單,這個時候,我們需要將庫存+1,吧這個名額給其他顧客繼續搶購,那么這如何實現?是直接將庫存加回去嗎?
站在數據庫層面,我們對于一件商品的庫存數據,如果多個個顧客同時請求下單,這個時候,我們怎么處理這種多線程環境下,保證不會超出庫存量?
每個用戶都會去不停的刷新商品,不停地關注商品的庫存量,想看看現在庫存是否有,我是否能下單?這種高請求量的讀請求和其他用戶的高請求量的下單操作并發出現,我們如何控制一條數據讀寫操作并發環境下正常?那這種情況下,我們既要保證有序進行,又要保證系統的高吞吐性能?
?
上面拋出來了一些問題,我們接下來都會一一分析,一一思考解決方案;
?
對于上述問題,我們需要考慮整體的架構設計,因為對于一個系統的健壯性,不能單單考慮一個點,依賴于某個技術棧就能解決問題;
對于系統的健壯性,高可用,業務應用集群部署是少不了的,通常來說,一般的互聯網項目,我們都是線上部署多個應用集群,通過負載均衡規則,將流量均攤給各個服務。保證整體業務的高可用。這是一種思路,但是我們今天從另外一個角度來分析一下系統優化。
很多后端程序員都會片面思考服務端如何優化,怎么保證系統的健壯,但是我認為這是不好的思維方式(雖然筆者側重后端開發);作為一個優秀的程序員,應當有全局設計的思考能力。
通常來說,我們一個B/S應用結構,是由用戶電腦的瀏覽器發起請求,然后由統一網關接收,然后將其路由至對應的服務應用上處理,然后服務應用訪問數據庫的數據。一個由上至下的層次訪問方式。(應該不會有人問直接瀏覽器訪問數據庫吧?)畫個圖
?
很簡單的一個架構方式,我們就需要對這圖上的各個節點進行系統優化思考
?
用戶的瀏覽器
對于頂層,直接和用戶打交道的,通常交互應用是通過應用客戶端,和瀏覽器網頁等形式,在這層上,我們通常都會有一定的處理邏輯。H5上會有js腳本,控制整個網頁的規則運行,APP上通常有類似java等語言處理邏輯。我們都是通過這些邏輯再與服務器進行交互請求,回調的。那么在此處,我們就需要限制用戶的無意或者惡意操作了。例如,用戶狂刷新商品,看看商品的最新庫存(本質來說,這是很常見的用戶操作行為,但是在筆者看來,這無意義人工壓測了)。那么這種操作,我們就需要控制真正請求到后端的請求量了。如果客戶端是一個吃瓜群眾,用戶操作多少下,就請求多少下,那么這個客戶端開發就會被服務端打死。
我們會看到很多類似的場景,例如,一個下單按鈕,我們會在正常發起了一個下單請求后,當服務端還未返回結果前,這個按鈕就是置灰的,不允許點擊的。這也是降頻操作。
但是對于一些操作場景,讀的場景,我們不可能說,用戶去刷新商品的庫存,一秒鐘只能刷新一次(至少不能讓用戶這樣感知),這個時候,我們就需要做一些特殊處理了。例如,采用客戶端本地緩存,舉例說明,對于某個商品的庫存數據,客戶端采用緩存,緩存的過期時間為2s,當緩存過期后,才再次真實的向服務端發起請求,那么原本用戶在10s中共刷新了200下商品緩存,那么這個時候,其實只會發起5個請求到服務端,原本需要200個請求,攔截了大量的惡意用戶操作請求。并且我們并不限制用戶的操作,提高的用戶的體驗感。
平時我們也會看到很多搶購活動中,明明有庫存啊,就是下單的時候,就告訴我,沒貨了。這其實就是緩存一致性問題導致的。這種問題都是我們故意制造出來的,因為在這種業務場景下,我們是不太特別嚴格要求數據的一致性的。
?
網關
上面關于普通的用戶操作,我們是可以處理大部分的用戶的惡意操作的。但是在一個合格的程序員面前,我們所考慮的用戶并不都是那種"良好"操作習慣的用戶。對于系統安全來說,我們還需要考慮“同行”的行為。
我們的12306搶票軟件,都知道,我們很多人大多時候并不會真的在這個軟件平臺上進行購票操作,因為很多時候我們在上面看,基本上都是沒票。
那么這個時候就衍生出來了很多“同行”的產品,例如“智行12306”、“美團搶票”等等,那么對于這種第三方平臺所制作出來的應用,目的是什么?是為了保護"官方12306購票系統"嗎?當然不是,他們的目的就是保證他們的付費用戶能夠高效的購買的心意的票。那么這個時候,就是不是用戶與服務之間的較量了。這就是服務與服務之間的較量了。并且這種第三方應用很多,這個時候又變成“多個打一個”了。你覺得"官方12306購票系統"難不難?這也是為什么早期"官方12306購票系統"上線的時候,直接崩了的原因了。
那么"官方12306購票系統"怎么處理呢?不管你還是“智行12306”、“美團搶票”,你都需要登錄吧?不登錄,你還想搶票?當游客嗎?那么這個時候,我們就可以有效的過濾一批請求了。我們的 12306賬戶都是實名制的,一個真實的合法公民只能有一個賬號,綁定了我們的身份證,那么這個時候,我們就能確定一個用戶發起的所有請求是否只是一個用戶發起的,我們就可以針對這個用戶進行精準限流了,在用戶發起下單請求的時候,都需要攜帶用戶ID,那么這個時候,我們在站點層面就可以控制一個用戶ID只路由一個請求到后端中,其他請求,站點都可以拋棄,直接返回false了。這樣,同一個用戶不管1秒發起的10000個下單請求還是10個下單請求,只會有一個請求被放行。這樣即使有些用戶使用了第三方搶票軟件,我們也可以控制每個用戶之間的請求是公平的。
甚至我們還可以做一些惡意攻擊的處理方案,例如當1s發起了1000個請求,將其用戶ID放入小黑屋待一段時間(布隆過濾器),或者將這個主機ip加進去,這樣的話,就又控制了一批非人為操作發起的請求。
上面說的是寫請求,那么獲取請求呢?返回false?那這個時候也是不合理的,我們這個時候還是需要考慮緩存,站點層面的數據緩存,例如,用戶獲取當前商品的庫存,這個時候,我們不以用戶ID作為緩存鍵,而是以用戶請求的商品的ID為緩存鍵,這樣,即使兩個不同的用戶發起的請求,我們也可以實現緩存共用了。當用戶1查詢火車A的票余量的時候,生成的緩存,用戶2也去查詢火車B的票余量的時候,當超出限額流量的時候,也返回這個緩存數據,那么真實的請求就不會直接路由到業務服務上去。這也是一種保障用戶友好體驗的行為了。如果我們直接拋“請求過快,請稍后再重試”,你煩嗎?我挺煩的。
對于此處,我們可以考慮“七層負載均衡”,通過nginx實現。如果想提高系統的吞吐率,我們可以使用增加節點方式,增加系統的吞吐率性能。
?
業務應用
存在一種這樣的情況,服務已用戶的賬戶進行限流,那么這個時候,如果我用1000個賬戶去搶票呢?其他用戶只能用自己的1個賬戶去搶票,那么我的搶票的成功的概率是不是相對高一些呢?那這個時候,系統的并發也會有1000,再結合真實的人為產生的并發,也是有很高的并發量,那這個時候對于系統而言,怎么控制順序讀寫?
mysql官方宣稱寫操作,單機并發2000寫操作極限。再多就爆了。
那么redis官方宣稱單機并發80000寫操作,那這就可以解決很多流量問題了。
然后我們需要真實的控制可控的請求到數據庫層面,那么如何控制呢?也就是緩存限流。
票的余量有1000張,10000個請求如果全部請求到數據庫中?后面的9000個請求都直接返回票已賣光,這樣不就可以嗎?可以通過緩存記錄票余量,直接在緩存中操作數量的讀寫操作,對于緩存的數據我們采用異步入庫操作。定時將票的余量同步至數據庫中。這樣也保證了數據的持久性。 那么怎么控制買票的有序執行呢?隊列,我們可以通過有序隊列,消費下單請求,這樣也保護了數據庫的安全性。不使用隊列,直接通過一個狀態進行記錄當前售票的情況,當我們記錄到第1100個請求后,就直接將“票是否售完”狀態變為true,那么這個時候,后面的請求都直接返回“票已售光”,那么就降低了系統邏輯的處理負載。
當然,對于業務應用,我們可以通過產品規則去解決,通過一些案例去解決。
例如:分時段操作,將用戶從產品設計上進行拆分,這也是一種分散流量的操作。
對于"用戶取消訂單"的這種操作,我們可以將緩存中的票的余量進行重建,將票的余量+1,然后再將“票是否售完”狀態變為false,這個時候我們會驚奇的發現,過了1個小時了,突然發現又有票了,奇怪哈!
在雙十一的時候,淘寶也做了一個操作,就是“雙十一當天禁止取消訂單”操作,只能通過攔截方式。這是屬于產品設計上進行系統保護。
?
數據庫
從數據庫層面而言,我們只需要考慮幾點:保證數據庫的可用性、保證數據的持久性、保證業務數據的正確性(不能賣票賣到還剩-10張吧?)
對于數據庫的特性而言,存在行鎖,多條請求,我們在多個請求的時候,當一個請求在修改數據庫的數據的時候,就會出現排他鎖,其他操作都是阻塞的,那么我們如果解決呢?排他鎖,能夠保證業務的正常,事務的隔離性,數據的臟讀、幻讀、重復讀等異常情況,顯然,我們不能直接不用innodb,對于互聯網項目而言,讀多寫少,我們還是需要考慮事務問題,那么這個時候,我們就可以對數據進行拆分了。當票的余量的表的數據過大,我們可以考慮水平分表,分庫,通過地域進行拆分。用售票規則而定,假如一張“杭州東” ~ “南昌西”的車次的票,我們可以將其放在“浙江庫的杭州表”中,那么杭州始發地的票的余額,我們都可以知道去哪里查詢,那么對于這趟車次的用戶假設也很多呢?我們怎么解決一條數據的并發寫操作呢?這里我們可以考慮將始發地進行拆分。都知道,我們12306放票規則是根據始發地分配的。
“杭州東” ~ “南昌西”
會途徑 “金華”/"上饒" ,那么這個時候,我們假設這趟車一共有1000張票,那么“杭州東” 可以賣500張,“金華”可以賣 300張 “上饒”可以賣200張(當然,賣火車票不是這么簡單的,我們需要考慮出發地,目的地,才能知道區間的票的購買余量,并且還需要控制始發地的放票量,才能給中間的站點買票數量)。那么這樣我們就可以將一個車次的票拆分為3條數據,這樣,3條數據就便于我們寫操作了。
對于上述的業務而言,存在不嚴謹的數據假設,此處也只是為了舉例思考。歡迎各位舉例批評。
?
?
總結:
以上為個人通過個人學習,網絡大佬的分享,總結的一些思考,希望各位bu she li jiao (沒打出來這個成語,尷尬了)
總結
以上是生活随笔為你收集整理的什么是“秒杀”?为什么传统项目中也有“秒杀”的概念?一起来分析一下.的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 先进过程控制之一:浅说APC
- 下一篇: 传智健康 ----- 移动端开发 (体检