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

解密高并發(fā)業(yè)務(wù)場(chǎng)景下典型的秒殺系統(tǒng)的架構(gòu)

【源碼版】基于SpringMVC的電商高并發(fā)秒殺系統(tǒng)設(shè)計(jì)思路

如何實(shí)現(xiàn)一個(gè)秒殺系統(tǒng)
通過(guò)秒殺商品來(lái)模擬高并發(fā)的場(chǎng)景
服務(wù)器的高并發(fā)能力如何提升?
如何控制秒殺商品頁(yè)面購(gòu)買(mǎi)按鈕的點(diǎn)亮

服務(wù)器并發(fā)的概念

java結(jié)合redis秒殺功能
redis高并發(fā)能力直接相關(guān)概念有哪些
高并發(fā)系統(tǒng)的藝術(shù):如何在流量洪峰中游刃有余

高并發(fā)物聯(lián)網(wǎng)云平臺(tái)是什么
TurMass? 如何幫助解決 UWB 定位系統(tǒng)大規(guī)模終端標(biāo)簽高并發(fā)通信沖突問(wèn)題?

評(píng)論