高并發(fā)秒殺系統(tǒng)總結(jié)
大家也許開發(fā)過高并發(fā)的系統(tǒng)或者秒殺程序,但肯定都有接觸過,像電商平臺的秒殺、搶購等活動(dòng),還有12306春運(yùn)搶票。
活動(dòng)周期短,瞬間流量大(高并發(fā)),技術(shù)在這種情況下,會(huì)發(fā)生和要做的事。
第一:高并發(fā)
技術(shù)要做的事,一方面優(yōu)化程序,讓程序性能最優(yōu),單次請求時(shí)間能從50ms優(yōu)化到25ms,那就可以在一秒鐘內(nèi)成功響應(yīng)翻倍的請求了。
另一方面就是增加服務(wù)器,用更大的集群來處理用戶請求,設(shè)計(jì)好一個(gè)可靠且靈活擴(kuò)充的分布式方案就更加重要了。
第二:時(shí)間短
火熱的秒殺活動(dòng),真的是一秒鐘以內(nèi)就會(huì)把商品搶購一空,而大部分用戶的感受是,提交訂單的過程卻要等待好幾秒、甚至十幾秒,更糟糕的當(dāng)然是請求報(bào)錯(cuò)。
那么一個(gè)好的秒殺體驗(yàn),當(dāng)然希望盡可能減少用戶等待時(shí)間,準(zhǔn)確的提示用戶當(dāng)前是否還有商品庫存。而這些,也是需要有優(yōu)秀的程序設(shè)計(jì)來保證的。
第三:系統(tǒng)容量預(yù)估
系統(tǒng)設(shè)計(jì)的時(shí)候,都需要有一個(gè)容量預(yù)估,那就是要提前計(jì)算好,我們設(shè)計(jì)的系統(tǒng),要承載多大的數(shù)量級。
假如線上前端服務(wù)器規(guī)格是8核16G內(nèi)存的服務(wù)器,而提交訂單的處理程序耗時(shí)100ms,那么可以簡單計(jì)算一下:
每秒可以處理的訂單請求數(shù)=1000ms/100ms*8=80qps |
上面這個(gè)結(jié)果,對于秒殺系統(tǒng)來說,肯定是非常不理想的。
如果能將處理程序耗時(shí)優(yōu)化后,降低到10ms,那么就可以達(dá)到800qps。
如果我們可以把程序繼續(xù)優(yōu)化,能快速區(qū)分開有庫存和無庫存處理,那么無庫存時(shí)處理就有可能做到1ms甚至更低的耗時(shí)。這樣無庫存時(shí)就能有更好的性能,上萬的qps也是可以達(dá)到的。
上面的預(yù)估,都是針對單機(jī),那么簡單的增加前端服務(wù)器,是不是就能有更好的并發(fā)處理量呢?
肯定沒這么簡單,因?yàn)閿?shù)據(jù)庫、緩存系統(tǒng)甚至機(jī)房網(wǎng)絡(luò)帶寬都會(huì)成為瓶頸。
于是就要有一個(gè)更好的分布式方案。
第四:好的分布式方案
一個(gè)好的分布式方案,首先當(dāng)然是穩(wěn)定可靠,不要出亂子,然后就是方便擴(kuò)充,最好的效果當(dāng)然是增加一臺服務(wù)器,并發(fā)處理量可以1:1線性增長。
比如:單機(jī)qps是1k,那么10臺服務(wù)器可以做到1w,100臺可以做到10w每秒。
要做到這樣的線性增長效果,就要杜絕出現(xiàn)瓶頸,否則還是會(huì)代價(jià)太大。
拒絕假的分布式尤其重要,比如:前端服務(wù)器是可以獨(dú)立存在的,但是都依賴集中的一個(gè)數(shù)據(jù)庫或者緩存系統(tǒng),那最后,一定是集中的那個(gè)數(shù)據(jù)庫或者緩存系統(tǒng)受不了,同樣無法做到一個(gè)好的分布式。
第五:關(guān)注系統(tǒng)的瓶頸
大家先有幾個(gè)基本的共識,系統(tǒng)的處理速度
- 程序內(nèi)數(shù)據(jù)讀寫 > redis > mysql > 磁盤
- 單機(jī)網(wǎng)絡(luò)請求 > 局域網(wǎng)內(nèi)請求 > 跨機(jī)房請求
我們優(yōu)化程序的時(shí)候,盡量用最快的方式,盡量用最簡短的邏輯。
用redis替代mysql來保存訂單處理中依賴的數(shù)據(jù),用程序中的提交的數(shù)據(jù)代替從redis中二次獲取數(shù)據(jù),比如:商品庫存信息,用戶訂單信息。
邏輯處理中,把速度快且提前中斷的邏輯放在最前面,比如:驗(yàn)證登錄,驗(yàn)證問答。
我們做分布式方案的時(shí)候,盡量把資源調(diào)用放在最近的地方。
前端服務(wù)器依賴的數(shù)據(jù)盡量就在局域網(wǎng)內(nèi),如果能在單機(jī)都有讀的redis服務(wù)當(dāng)然更好,程序維護(hù)數(shù)據(jù)響應(yīng)會(huì)復(fù)雜些。
不要出現(xiàn)跨機(jī)房網(wǎng)絡(luò)請求,不要出現(xiàn)跨機(jī)房網(wǎng)絡(luò)請求,不要出現(xiàn)跨機(jī)房網(wǎng)絡(luò)請求,重要的事情說三遍。
第六:什么語言更適合這類系統(tǒng)
課程中用的是PHP語言,開發(fā)這類系統(tǒng)也是沒問題的。
當(dāng)然,像是用golang, ngx_lua可能在高并發(fā)和性能方面會(huì)更有優(yōu)勢。
如果使用java、.net當(dāng)然也是可以的,作為一個(gè)系統(tǒng),語言只是工具,更好的設(shè)計(jì)和優(yōu)化,才能達(dá)到最終想要的效果。
有了上面的基本概念,我們接下來再來看看,具體運(yùn)行時(shí),會(huì)出現(xiàn)什么狀況。
下面是一些具體的問題:
問題1:庫存超賣
只有10個(gè)庫存,但是一秒鐘有1k個(gè)訂單,怎么能不超賣呢?
核心思想就是保證庫存遞減是原子性操作,10--返回9,9--返回8,8--返回7。
而不能是讀取出來庫存10,10-1=9再更新回去。因?yàn)檫@個(gè)讀取和更新是并發(fā)執(zhí)行的,很可能就會(huì)有1k個(gè)訂單都成功了,而庫存實(shí)際只有10。
那么,怎么保證原子性操作呢?
1. 數(shù)據(jù)庫:
- update product set left_numleft_num=left_num-1 where left_num>0;
這里用到的是left_num=left_num-1,如果left_num>0才能執(zhí)行成功,數(shù)據(jù)庫查詢、更新的時(shí)候有用到鎖,是可以保證更新操作的原子性的。
數(shù)據(jù)庫性能較差,不建議使用。
2. 分布式鎖
用redis來做一個(gè)分布式鎖,reids->setnx('lock', 1) 設(shè)置一個(gè)鎖,程序執(zhí)行完成再del這個(gè)鎖。
鎖定的過程,不利于并發(fā)執(zhí)行,大家都在等待鎖解開,不建議使用。
3. 消息隊(duì)列
將訂單請求全部放入消息隊(duì)列,然后另外一個(gè)后臺程序一個(gè)個(gè)處理隊(duì)列中的訂單請求。
并發(fā)不受影響,但是用戶等待的時(shí)間較長,進(jìn)入隊(duì)列的訂單也會(huì)很多,體驗(yàn)上并不好,也不建議使用。
4. redis遞減
通過 redis->incrby('product', -1) 得到遞減之后的庫存數(shù)。
性能方面很好,同時(shí)體驗(yàn)上也很好,在PHP秒殺課程中,優(yōu)化后就是用的這種方法,而沒有使用上述其他方法,大家應(yīng)該也能對比了解啦。
問題2:集群怎么來規(guī)劃
前端服務(wù)器因?yàn)闆]有相互間關(guān)聯(lián),集群的數(shù)量不受影響。
redis的性能可以達(dá)到每秒幾萬次響應(yīng),所以一個(gè)集群的規(guī)模,也就是redis服務(wù)可以承載的數(shù)量。
比如:一臺前端服務(wù)器是1~2k的qps(有庫存時(shí)),那么10臺+1臺redis就可以是一個(gè)獨(dú)立的集群,可以支撐1~2w每秒訂單量。
10個(gè)上述的集群就可以做到一秒鐘處理10w~20w的有效訂單。
如果秒殺活動(dòng)的庫存量在1w以內(nèi),預(yù)計(jì)參與的人數(shù)在百萬左右,那么有一個(gè)集群也就可以搞定。
如果秒殺參與的人數(shù)超過千萬,那么就要用到不止一個(gè)集群了。
問題3:多個(gè)集群的數(shù)據(jù)怎么保持一致性
不要做多集群的數(shù)據(jù)同步,而是用散列,每個(gè)集群的數(shù)據(jù)是獨(dú)立存在的。
假設(shè),有10個(gè)商品,每個(gè)商品有1w庫存,規(guī)劃用10個(gè)集群,那么每個(gè)集群有10個(gè)商品,每個(gè)商品是1k庫存。
每個(gè)集群只需要負(fù)責(zé)把自己的庫存賣掉即可,至于說,會(huì)不會(huì)有用戶知道有10個(gè)集群,然后每個(gè)集群都去搶。
這種情況就不要用程序來處理了,利用運(yùn)營規(guī)則,活動(dòng)結(jié)束后匯總訂單的時(shí)候再去處理就好了。
如果擔(dān)心散列的不合理,比如:某個(gè)集群用戶訪問量特別少,那么可以引入一個(gè)中控服務(wù),來監(jiān)控各個(gè)集群的庫存,然后再做平衡。
問題4:機(jī)器人搶購怎么辦:
沒什么太好的辦法,類似DDOS攻擊,只能是讓自身更強(qiáng)大才是王道。
運(yùn)營策略上,可以嚴(yán)格控制用戶注冊,必須登錄,提交訂單的時(shí)候引入圖像驗(yàn)證碼,問答,交互式驗(yàn)證等。