大話程序猿眼里的高并發(fā)(轉(zhuǎn))

本文轉(zhuǎn)載自http://blog.thankbabe.com/2016/04/01/high-concurrency/

高并發(fā)是指在同一個(gè)時(shí)間點(diǎn),有很多用戶同時(shí)的訪問URL地址,比如:淘寶的雙11,雙12,就會(huì)產(chǎn)生高并發(fā),如貼吧的爆吧,就是惡意的高并發(fā)請(qǐng)求,也就是DDOS攻擊,再屌絲點(diǎn)的說法就像玩擼啊擼被ADC暴擊了一樣,那傷害你懂得(如果你看懂了,這個(gè)說法說明是正在奔向人生巔峰的屌絲。
高并發(fā)會(huì)來帶的后果
服務(wù)端:導(dǎo)致站點(diǎn)服務(wù)器/DB服務(wù)器資源被占滿崩潰,數(shù)據(jù)的存儲(chǔ)和更新結(jié)果和理想的設(shè)計(jì)是不一樣的,比如:出現(xiàn)重復(fù)的數(shù)據(jù)記錄,多次添加了用戶積分等。

用戶角度:尼瑪,這么卡,老子來參加活動(dòng)的,刷新了還是這樣,垃圾網(wǎng)站,再也不來了。

我的經(jīng)歷:在做公司產(chǎn)品網(wǎng)站的過程中,經(jīng)常會(huì)有這樣的需求,比如什么搞個(gè)活動(dòng)專題,抽獎(jiǎng),簽到,搞個(gè)積分競拍等等,如果沒有考慮到高并發(fā)下的數(shù)據(jù)處理,那就Game Over了,很容易導(dǎo)致抽獎(jiǎng)被多抽走,簽到會(huì)發(fā)現(xiàn)一個(gè)用戶有多條記錄,簽到一次獲得了獲得了多積分,等等,各種超出正常邏輯的現(xiàn)象,這就是做產(chǎn)品網(wǎng)站必須考慮的問題,因?yàn)檫@些都是面向大量用戶的,而不是像做ERP管理系統(tǒng),OA系統(tǒng)那樣,只是面向員工。

下面我進(jìn)行實(shí)例分析,簡單粗暴,動(dòng)態(tài)分析,純屬本人個(gè)人經(jīng)驗(yàn)分享,如有說錯(cuò),或者有更好的建議或者意見的請(qǐng)留言,大家一起成長。

并發(fā)下的數(shù)據(jù)處理:
通過表設(shè)計(jì),如:記錄表添加唯一約束,數(shù)據(jù)處理邏輯使用事物防止并發(fā)下的數(shù)據(jù)錯(cuò)亂問題通過服務(wù)端鎖進(jìn)程防止包并發(fā)下的數(shù)據(jù)錯(cuò)亂問題
這里主要講述的是在并發(fā)請(qǐng)求下的數(shù)據(jù)邏輯處理的接口,如何保證數(shù)據(jù)的一致性和完整性,這里的并發(fā)可能是大量用戶發(fā)起的,也可能攻擊者通過并發(fā)工具發(fā)起的并發(fā)請(qǐng)求

如例子:通過表設(shè)計(jì)防止并發(fā)導(dǎo)致數(shù)據(jù)錯(cuò)亂

需求點(diǎn)【簽到功能】 一天一個(gè)用戶只能簽到一次,簽到成功后用戶獲取到一個(gè)積分
已知表用戶表,包含積分字段高并發(fā)意淫分析(屬于開發(fā)前的猜測(cè)):在高并發(fā)的情況下,會(huì)導(dǎo)致,一個(gè)用戶簽到記錄會(huì)有多條,或者用戶簽到后不止加一積分。
我的設(shè)計(jì)首先根據(jù)需求我會(huì)添加一張簽到記錄表,重點(diǎn)來了,這張表需要把用戶唯一標(biāo)識(shí)字段(ID,Token)和簽到日期字段添加為唯一約束,或者唯一索引,這樣就可以防止并發(fā)的時(shí)候插入重復(fù)用戶的簽到記錄。然后再程序代碼邏輯里,先執(zhí)行簽到數(shù)據(jù)的添加(這里可以防止并發(fā),添加成功后再進(jìn)行積分的添加,這樣就可以防止重復(fù)的添加積分了。最后我還是建議所有的數(shù)據(jù)操作都寫在一個(gè)sql事務(wù)里面, 這樣在添加失敗,或者編輯用戶積分失敗的時(shí)候可以回滾數(shù)據(jù)。

如例子2(事務(wù)+通過更新鎖 防止并發(fā)導(dǎo)致數(shù)據(jù)錯(cuò)亂 或者事物+Update的鎖表機(jī)制)

需求點(diǎn):【抽獎(jiǎng)功能】抽獎(jiǎng)一次消耗一個(gè)積分抽獎(jiǎng)中獎(jiǎng)后編輯剩余獎(jiǎng)品總數(shù)剩余獎(jiǎng)品總數(shù)為0,或者用戶積分為0的時(shí)候無法進(jìn)行抽獎(jiǎng)
已知表:用戶表,包含積分字段獎(jiǎng)品表,包含獎(jiǎng)品剩余數(shù)量字段
高并發(fā)意淫分析(屬于開發(fā)前的猜測(cè)):在高并發(fā)的情況下,會(huì)導(dǎo)致用戶參與抽獎(jiǎng)的時(shí)候積分被扣除,而獎(jiǎng)品實(shí)際上已經(jīng)被抽完了
我的設(shè)計(jì):在事物里,通過WITH (UPDLOCK) 鎖住商品表,或者Update 表的獎(jiǎng)品剩余數(shù)量和最后編輯時(shí)間字段,來把數(shù)據(jù)行鎖住,然后進(jìn)行用戶積分的消耗,都完成后提交事物,失敗就回滾。這樣就可以保證,只有可能存在一個(gè)操作在操作這件商品的數(shù)量,只有等到這個(gè)操作事物提交后,其他的操作這個(gè)商品行的事物才會(huì)繼續(xù)執(zhí)行。

如例子3(通過程序代碼防止包并發(fā)下的數(shù)據(jù)錯(cuò)亂問題)

需求點(diǎn):【緩存數(shù)據(jù)到cache里】,當(dāng)緩存不存在的時(shí)候,從數(shù)據(jù)庫中獲取并保存在cache里,如果存在從cache里獲取,每天10點(diǎn)必須更新一次,其他時(shí)間點(diǎn)緩存兩個(gè)小時(shí)更新一次到10點(diǎn)的時(shí)候,凡是打開頁面的用戶會(huì)自動(dòng)刷新頁面
問題點(diǎn):這里有個(gè)邏輯用戶觸發(fā)緩存的更新,用戶刷新頁面,當(dāng)緩存存在的時(shí)候,會(huì)取到最后一次緩存更新時(shí)間,如果當(dāng)前時(shí)間大于十點(diǎn),并且最后緩存時(shí)間是10點(diǎn)前,則會(huì)從數(shù)據(jù)庫中重新獲取數(shù)據(jù)保存到cache中。還有客戶端頁面會(huì)在10點(diǎn)時(shí)候用js發(fā)起頁面的刷新,就是因?yàn)橛羞@樣的邏輯,導(dǎo)致10點(diǎn)的時(shí)候有很多并發(fā)請(qǐng)求同時(shí)過來,然后就會(huì)導(dǎo)致很多的sql查詢操作,理想的邏輯是,只有一個(gè)請(qǐng)求會(huì)去數(shù)據(jù)庫獲取,其他都是從緩存中獲取數(shù)據(jù)。(因?yàn)檫@個(gè)sql查詢很耗服務(wù)器性能,所以導(dǎo)致在10點(diǎn)的時(shí)候,突然間數(shù)據(jù)庫服務(wù)器壓力暴增)
解決問題:C#通過 (鎖)lock,在從數(shù)據(jù)讀取到緩存的那段代碼前面加上鎖,這樣在并發(fā)的情況下只會(huì)有一個(gè)請(qǐng)求是從數(shù)據(jù)庫里獲取數(shù)據(jù),其他都是從緩存中獲取。

訪問量大的數(shù)據(jù)統(tǒng)計(jì)接口
需求: 用戶行為數(shù)據(jù)統(tǒng)計(jì)接口,用來記錄商品展示次數(shù),用戶通過點(diǎn)擊圖片,或者鏈接,或者其他方式進(jìn)入到商品詳情的行為次數(shù)
問題點(diǎn):這接口是給前端ajax使用,訪問量會(huì)很大,一頁面展示的時(shí)候就會(huì)有幾十件商品的展示,滾動(dòng)條滾到到頁面顯示商品的時(shí)候就會(huì)請(qǐng)求接口進(jìn)行展示數(shù)據(jù)的統(tǒng)計(jì),每次翻頁又會(huì)加載幾十件
意淫分析:設(shè)想如果同時(shí)有1W個(gè)用戶同時(shí)在線訪問頁面,一個(gè)次拉動(dòng)滾動(dòng)條屏幕頁面展示10件商品,這樣就會(huì)有10W個(gè)請(qǐng)求過來,服務(wù)端需要把請(qǐng)求數(shù)據(jù)入庫。在實(shí)際線上環(huán)境可能還會(huì)超過這個(gè)請(qǐng)求量,如果不經(jīng)過進(jìn)行高并發(fā)設(shè)計(jì)處理,服務(wù)器分分鐘給跪了。
解決問題:我們通過nodejs寫了一個(gè)數(shù)據(jù)處理接口,把統(tǒng)計(jì)數(shù)據(jù)先存到redis的list里。(使用nodejs寫接口的好處是,nodejs使用單線程異步事件機(jī)制,高并發(fā)處理能力強(qiáng),不會(huì)因?yàn)閿?shù)據(jù)邏輯處理問題導(dǎo)致服務(wù)器資源被占用而導(dǎo)致服務(wù)器宕機(jī))然后再使用nodejs寫了一個(gè)腳本,腳本功能就是從redis里出列數(shù)據(jù)保存到mysql數(shù)據(jù)庫中。這個(gè)腳本會(huì)一直運(yùn)行,當(dāng)redis沒有數(shù)據(jù)需要同步到數(shù)據(jù)庫中的時(shí)候,sleep,讓在進(jìn)行數(shù)據(jù)同步操作

高并發(fā)的下的服務(wù)器壓力均衡,合理站點(diǎn)架設(shè),DB部署
以下我所知道的:

服務(wù)器代理nginx,做服務(wù)器的均衡負(fù)載,把壓力均衡到多臺(tái)服務(wù)器
部署集群 mysql數(shù)據(jù)庫, redis服務(wù)器,或者mongodb服務(wù)器,把一些常用的查詢數(shù)據(jù),并且不會(huì)經(jīng)常的變化的數(shù)據(jù)保存到其他nosql DB服務(wù)器中,來減少數(shù)據(jù)庫服務(wù)器的壓力,加快數(shù)據(jù)的響應(yīng)速度。
數(shù)據(jù)緩存,Cache
在高并發(fā)接口的設(shè)計(jì)中可以使用具有高并發(fā)能力的編程語言去開發(fā),如:nodejs 做web接口
服務(wù)器部署,圖片服務(wù)器分離,靜態(tài)文件走CDN
DBA數(shù)據(jù)庫的優(yōu)化查詢條件,索引優(yōu)化
消息存儲(chǔ)機(jī)制,將數(shù)據(jù)添加到信息隊(duì)列中(redis list),然后再寫工具去入庫
腳本合理控制請(qǐng)求,如,防止用戶重復(fù)點(diǎn)擊導(dǎo)致的ajax多余的請(qǐng)求,等等。

并發(fā)測(cè)試神器推薦
Apache JMeter
Microsoft Web Application Stress Tool
Visual Studio 性能負(fù)載

感謝大家的支持,領(lǐng)取天貓雙12紅包,獲得推廣費(fèi)用來維持網(wǎng)站的運(yùn)行,謝謝理解。 [去領(lǐng)取] 轉(zhuǎn)載請(qǐng)申明原文地址,謝謝合作如有任何想說的請(qǐng)留言哦,我會(huì)根據(jù)大家的建議修改有疑義的內(nèi)容歡迎大家關(guān)注我的 Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容