高併發是指在同一個時間點,有很多使用者同時的訪問URL地址,比如:淘寶的雙11,雙12,就會產生高併發,如貼吧的爆吧,就是惡意的高併發請求,也就是DDOS攻擊,再屌絲點的說法就像玩擼啊擼被ADC暴擊了一樣,那傷害你懂得(如果你看懂了,這個說法說明是正在奔向人生巔峰的屌絲。
高併發會來帶的後果
- 服務端:
導致站點伺服器/DB伺服器資源被佔滿崩潰,資料的儲存和更新結果和理想的設計是不一樣的,比如:出現重複的資料記錄,多次新增了使用者積分等。
- 使用者角度:
尼瑪,這麼卡,老子來參加活動的,重新整理了還是這樣,垃圾網站,再也不來了。
- 我的經歷:
在做公司產品網站的過程中,經常會有這樣的需求,比如什麼搞個活動專題,抽獎,簽到,搞個積分競拍等等,如果沒有考慮到高併發下的資料處理,那就Game Over了,很容易導致抽獎被多抽走,簽到會發現一個使用者有多條記錄,簽到一次獲得了獲得了多積分,等等,各種超出正常邏輯的現象,這就是做產品網站必須考慮的問題,因為這些都是面向大量使用者的,而不是像做ERP管理系統,OA系統那樣,只是面向員工。
下面我進行例項分析,簡單粗暴,動態分析,純屬本人個人經驗分享,如有說錯,或者有更好的建議或者意見的請留言,大家一起成長。
併發下的資料處理:
通過表設計,如:記錄表新增唯一約束,資料處理邏輯使用事務防止併發下的資料錯亂問題
通過服務端鎖程式防止併發下的資料錯亂問題
這裡主要講述的是在併發請求下的資料邏輯處理的介面,如何保證資料的一致性和完整性,這裡的併發可能是大量使用者發起的,也可能攻擊者通過併發工具發起的併發請求
如例子:通過表設計防止併發導致資料錯亂
- 需求點
【簽到功能】 一天一個使用者只能簽到一次,
簽到成功後使用者獲取到一個積分
- 已知表
使用者表,包含積分欄位
高併發意淫分析(屬於開發前的猜測):
在高併發的情況下,會導致,一個使用者簽到記錄會有多條,或者使用者簽到後不止加一積分。
- 我的設計
首先根據需求我會新增一張簽到記錄表,重點來了,這張表需要把使用者唯一標識欄位(ID,Token)和簽到日期欄位新增為唯一約束,或者唯一索引,這樣就可以防止併發的時候插入重複使用者的簽到記錄。然後再程式程式碼邏輯裡,先執行簽到資料的新增(這裡可以防止併發,新增成功後再進行積分的新增,這樣就可以防止重複的新增積分了。最後我還是建議所有的資料操作都寫在一個sql事務裡面, 這樣在新增失敗,或者編輯使用者積分失敗的時候可以回滾資料。
如例子2(事務+通過更新鎖 防止併發導致資料錯亂 或者事務+Update的鎖表機制)
- 需求點:
【抽獎功能】 抽獎一次消耗一個積分 抽獎中獎後編輯剩餘獎品總數 剩餘獎品總數為0,或者使用者積分為0的時候無法進行抽獎
- 已知表:
使用者表,包含積分欄位 獎品表,包含獎品剩餘數量欄位
- 高併發意淫分析(屬於開發前的猜測):
在高併發的情況下,會導致使用者參與抽獎的時候積分被扣除,而獎品實際上已經被抽完了
- 我的設計:
在事務裡,通過WITH (UPDLOCK) 鎖住商品表,或者Update 表的獎品剩餘數量和最後編輯時間欄位,來把資料行鎖住,然後進行使用者積分的消耗,都完成後提交事務,失敗就回滾。 這樣就可以保證,只有可能存在一個操作在操作這件商品的數量,只有等到這個操作事務提交後,其他的操作這個商品行的事務才會繼續執行。
如例子3(通過程式程式碼防止包併發下的資料錯亂問題)
- 需求點:
【快取資料到cache裡】, 當快取不存在的時候,從資料庫中獲取並儲存在cache裡,如果存在從cache裡獲取,每天10點必須更新一次,其他時間點快取兩個小時更新一次 到10點的時候,凡是開啟頁面的使用者會自動重新整理頁面
- 問題點:
這裡有個邏輯使用者觸發快取的更新,使用者重新整理頁面,當快取存在的時候,會取到最後一次快取更新時間,如果當前時間大於十點,並且最後快取時間是10點前,則會從資料庫中重新獲取資料儲存到cache中。 還有客戶端頁面會在10點時候用js發起頁面的重新整理,就是因為有這樣的邏輯,導致10點的時候有很多併發請求同時過來,然後就會導致很多的sql查詢操作,理想的邏輯是,只有一個請求會去資料庫獲取,其他都是從快取中獲取資料。(因為這個sql查詢很耗伺服器效能,所以導致在10點的時候,突然間資料庫伺服器壓力暴增)
- 解決問題:
C#通過 (鎖)lock,在從資料庫讀取到快取的那段程式碼前面加上鎖,這樣在併發的情況下只會有一個請求是從資料庫裡獲取資料,其他都是從快取中獲取。
訪問量大的資料統計介面
- 需求: 使用者行為資料統計介面,用來記錄商品展示次數,使用者通過點選圖片,或者連結,或者其他方式進入到商品詳情的行為次數
- 問題點:
這介面是給前端ajax使用,訪問量會很大,一頁面展示的時候就會有幾十件商品的展示,滾動條滾到頁面顯示商品的時候就會請求介面進行展示資料的統計,每次翻頁又會載入幾十件
- 意淫分析:
設想如果同時有1W個使用者同時線上訪問頁面,一個次拉動滾動條螢幕頁面展示10件商品,這樣就會有10W個請求過來,服務端需要把請求資料入庫。在實際線上環境可能還會超過這個請求量,如果不經過進行高併發設計處理,伺服器分分鐘給跪了。
- 解決問題:
我們通過nodejs寫了一個資料處理介面,把統計資料先存到redis的list裡。(使用nodejs寫介面的好處是,nodejs使用單執行緒非同步事件機制,高併發處理能力強,不會因為資料邏輯處理問題導致伺服器資源被佔用而導致伺服器當機) 然後再使用nodejs寫了一個指令碼,指令碼功能就是從redis裡取出對列資料儲存到mysql資料庫中。這個指令碼會一直執行,當redis沒有資料需要同步到資料庫中的時候,sleep,進行資料同步操作。
高併發的下的伺服器壓力均衡,合理站點架設,DB部署
以下我所知道的:
- 伺服器代理nginx,做伺服器的均衡負載,把壓力均衡到多臺伺服器
- 部署叢集 mysql資料庫, redis伺服器,或者mongodb伺服器,把一些常用的查詢資料,並且不會經常的變化的資料儲存到其他nosql DB伺服器中,來減少資料庫伺服器的壓力,加快資料的響應速度。
- 資料快取,Cache
- 在高併發介面的設計中可以使用具有高併發能力的程式語言去開發,如:nodejs 做web介面
- 伺服器部署,圖片伺服器分離,靜態檔案走CDN
- DBA資料庫的優化查詢條件,索引優化
- 訊息儲存機制,將資料新增到資訊佇列中(redis list),然後再寫工具去入庫
- 指令碼合理控制請求,如,防止使用者重複點選導致的ajax多餘的請求,等等。
併發測試神器推薦
- Apache JMeter
- Microsoft Web Application Stress Tool
- Visual Studio 效能負載