詳解秒殺系統(tǒng)設(shè)計的5個要點
秒殺系統(tǒng)涉及到的知識點
- 高并發(fā),cache,鎖機制
- 基于緩存架構(gòu)redis,Memcached的先進(jìn)先出隊列。
- 稍微大一點的秒殺,肯定是分布式的集群的,并發(fā)來自于多個節(jié)點的JVM,synchronized所有在JVM上加鎖是不行了
- 數(shù)據(jù)庫壓力
- 秒殺超賣問題
- 如何防止用戶來刷, 黑名單?IP限制?
- 利用memcached的帶原子性特性的操作做并發(fā)控制
秒殺簡單設(shè)計方案
比如有10件商品要秒殺,可以放到緩存中,讀寫時不要加鎖。 當(dāng)并發(fā)量大的時候,可能有25個人秒殺成功,這樣后面的就可以直接拋秒殺結(jié)束的靜態(tài)頁面。進(jìn)去的25個人中有15個人是不可能獲得商品的。所以可以根據(jù)進(jìn)入的先后順序只能前10個人購買成功。后面15個人就拋商品已秒殺完。
比如某商品10件物品待秒。假設(shè)有100臺web服務(wù)器(假設(shè)web服務(wù)器是Nginx + Tomcat),n臺app服務(wù)器,n個數(shù)據(jù)庫
第一步 如果Java層做過濾,可以在每臺web服務(wù)器的業(yè)務(wù)處理模塊里做個計數(shù)器AtomicInteger(10)=待秒商品總數(shù),decreaseAndGet()>=0的繼續(xù)做后續(xù)處理,<0的直接返回秒殺結(jié)束頁面,這樣經(jīng)過第一步的處理只剩下100臺*10個=1000個請求。
第二步,memcached 里以商品id作為key的value放個10,每個web服務(wù)器在接到每個請求的同時,向memcached服務(wù)器發(fā)起請求,利用memcached的decr(key,1)操作返回值>=0的繼續(xù)處理,其余的返回秒殺失敗頁面,這樣經(jīng)過第二步的處理只剩下100臺中最快速到達(dá)的10個請求。
第三步,向App服務(wù)器發(fā)起下單操作事務(wù)。
第四步,App服務(wù)器向商品所在的數(shù)據(jù)庫請求減庫存操作(操作數(shù)據(jù)庫時可以 "update table set count=count-1 where id=商品id and count>0;" update 成功記錄數(shù)為1,再向訂單數(shù)據(jù)庫添加訂單記錄,都成功后提交整個事務(wù),否則的話提示秒殺失敗,用戶進(jìn)入支付流程。
看看淘寶的秒殺
一、前端
面對高并發(fā)的搶購活動,前端常用的三板斧是【擴容】【靜態(tài)化】【限流】
擴容:加機器,這是最簡單的方法,通過增加前端池的整體承載量來抗峰值。
靜態(tài)化:將活動頁面上的所有可以靜態(tài)的元素全部靜態(tài)化,并盡量減少動態(tài)元素。通過CDN來抗峰值。
限流:一般都會采用IP級別的限流,即針對某一個IP,限制單位時間內(nèi)發(fā)起請求數(shù)量?;蛘呋顒尤肟诘臅r候增加游戲或者問題環(huán)節(jié)進(jìn)行消峰操作。
有損服務(wù):最后一招,在接近前端池承載能力的水位上限的時候,隨機拒絕部分請求來保護活動整體的可用性。
二、那么后端的數(shù)據(jù)庫在高并發(fā)和超賣下會遇到什么問題呢
- 首先MySQL自身對于高并發(fā)的處理性能就會出現(xiàn)問題,一般來說,MySQL的處理性能會隨著并發(fā)thread上升而上升,但是到了一定的并發(fā)度之后會出現(xiàn)明顯的拐點,之后一路下降,最終甚至?xí)葐蝨hread的性能還要差。
- 其次,超賣的根結(jié)在于減庫存操作是一個事務(wù)操作,需要先select,然后insert,最后update -1。最后這個-1操作是不能出現(xiàn)負(fù)數(shù)的,但是當(dāng)多用戶在有庫存的情況下并發(fā)操作,出現(xiàn)負(fù)數(shù)這是無法避免的。
- 最后,當(dāng)減庫存和高并發(fā)碰到一起的時候,由于操作的庫存數(shù)目在同一行,就會出現(xiàn)爭搶InnoDB行鎖的問題,導(dǎo)致出現(xiàn)互相等待甚至死鎖,從而大大降低MySQL的處理性能,最終導(dǎo)致前端頁面出現(xiàn)超時異常。
針對上述問題,如何解決呢? 淘寶的高大上解決方案:
I:關(guān)閉死鎖檢測,提高并發(fā)處理性能。
II:修改源代碼,將排隊提到進(jìn)入引擎層前,降低引擎層面的并發(fā)度。
III:組提交,降低server和引擎的交互次數(shù),降低IO消耗。
解決方案1: 將存庫從MySQL前移到Redis中,所有的寫操作放到內(nèi)存中,由于Redis中不存在鎖故不會出現(xiàn)互相等待,并且由于Redis的寫性能和讀性能都遠(yuǎn)高于MySQL,這就解決了高并發(fā)下的性能問題。然后通過隊列等異步手段,將變化的數(shù)據(jù)異步寫入到DB中。
優(yōu)點:解決性能問題
缺點:沒有解決超賣問題,同時由于異步寫入DB,存在某一時刻DB和Redis中數(shù)據(jù)不一致的風(fēng)險。
解決方案2: 引入隊列,然后將所有寫DB操作在單隊列中排隊,完全串行處理。當(dāng)達(dá)到庫存閥值的時候就不在消費隊列,并關(guān)閉購買功能。這就解決了超賣問題。
優(yōu)點:解決超賣問題,略微提升性能。
缺點:性能受限于隊列處理機處理性能和DB的寫入性能中最短的那個,另外多商品同時搶購的時候需要準(zhǔn)備多條隊列。
解決方案3: 將寫操作前移到MC中,同時利用MC的輕量級的鎖機制CAS來實現(xiàn)減庫存操作。
優(yōu)點:讀寫在內(nèi)存中,操作性能快,引入輕量級鎖之后可以保證同一時刻只有一個寫入成功,解決減庫存問題。
缺點:沒有實測,基于CAS的特性不知道高并發(fā)下是否會出現(xiàn)大量更新失???不過加鎖之后肯定對并發(fā)性能會有影響。
解決方案4: 將提交操作變成兩段式,先申請后確認(rèn)。然后利用Redis的原子自增操作,同時利用Redis的事務(wù)特性來發(fā)號,保證拿到小于等于庫存閥值的號的人都可以成功提交訂單。然后數(shù)據(jù)異步更新到DB中。
優(yōu)點:解決超賣問題,庫存讀寫都在內(nèi)存中,故同時解決性能問題。
缺點:由于異步寫入DB,可能存在數(shù)據(jù)不一致。另可能存在少買,也就是如果拿到號的人不真正下訂單,可能庫存減為0,但是訂單數(shù)并沒有達(dá)到庫存閥值。
總結(jié)
1、前端三板斧【擴容】【限流】【靜態(tài)化】
2、后端兩條路【內(nèi)存】+【排隊】
Github地址:github.com/Tyson0314/j…
以上就是詳解秒殺系統(tǒng)設(shè)計的5個要點的詳細(xì)內(nèi)容,更多關(guān)于秒殺系統(tǒng)設(shè)計的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MAC系統(tǒng)IDEA顏值插件MaterialThemeUI
俗話說,工欲善其事必先利其器。工具的顏值也很重要,好的主題讓人賞心悅目,有碼代碼的欲望。今天推薦一個IDEA顏值類插件:Material Theme UI2021-09-09chatGPT使用及注冊過程中常見的一些錯誤解決方法(所有報錯匯總)
這篇文章主要介紹了chatGPT注冊報錯及使用過程中報錯匯總及解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02JetBrains(IEDA、CLion、Pycharm) 學(xué)生獲得免費使用資格
JetBrains針對學(xué)生推出了免費使用資格,但是很多同學(xué)卻不知道或者說不知道怎樣獲得免費資格,通過學(xué)生認(rèn)證來使用JetBrains的軟件才是最方便穩(wěn)定的,具體怎么獲取呢,感興趣的朋友跟隨小編一起看看吧2020-08-08詳解scratch3.0二次開發(fā)之scratch-blocks中的blocks的類型、定義和使用方法
scratch-blocks是scratch-gui依賴的一個基本模塊,blocks的作用是通過拖曳的方法組成blocks堆塊,今天通過本文給大家分享scratch3.0二次開發(fā)之scratch-blocks的免編譯修改方法,感興趣的朋友一起看看吧2021-08-08