秒殺活動
秒殺採用什麼方案,redis掛了怎麼辦?
-100商品---》預熱---》100這個數,放到redis中----》incrby--》來一個秒殺請求-1,在redis集合中把使用者id放進去,最後100這個數變成了0,---》起個非同步任務---》消費集合中的id,生成訂單,扣減庫存,扣減賬戶餘額,提前充錢了
-使用者真去看訂單的時候---》非同步任務完成了下單
秒殺系統痛點有哪些?
1.高併發: 時間極短、 瞬間使用者量大,而且使用者會在開始前不斷重新整理頁面,還會積累一大堆重複請求的問題,請求過多把資料庫打宕機了,系統響應失敗,導致與這個系統耦合的系統也GG,一掛掛一片
2.連結暴露: 有人知道了你秒殺的url,然後在秒殺開始前通過這個url傳要傳遞的引數來進行購買操作
3.超賣: 你只有一百件商品,由於是高併發的問題,一起拿到了最後一件商品的資訊,都認為還有,全賣出去了,最終賣了一百十件,倉庫里根本沒這麼多貨
4.惡意請求: 因為秒殺的價格比較低,有人會用指令碼來秒殺,全給一個人買走了,他再轉賣,或者同時以指令碼操作多個賬號一起秒殺。(就是我們常見的黃牛黨)
5.資料庫: 一瞬間高QPS把資料庫打宕機了,誰都搶不到,活動失敗GG,這可能與高併發有重疊的點,不過著眼於資料庫的具體方面
解決方案
1.高併發的解決方案
1.nginx做負載均衡(一個tomcat可能只能抗住幾百的的併發,nginx還可以對一些惡意ip做限制) 2.資源靜態化,把前端的模板頁面放到CDN伺服器中(放到別的伺服器中,減輕自己伺服器的壓力) 3.頁面的按鈕,按一次後置灰X秒(防止一直使用者一直點,同一個使用者重複點選,雖然不會再賣給他,但是請求還是會到後端給系統壓力,需要在前端按鈕上做限制,比如點一下限制五秒內不能點選。) 4.同一個uid,限制訪問頻度,做頁面快取,x秒內到達站點層的請求,均返回同一頁面(用來防止其他程式設計師跳過按鈕,直接用for迴圈不斷髮起http請求,具體的話可以請求每次進來都去redis查有沒有對應的id的key值,沒有就在redis中設定X秒過期的key) 5.對於寫請求,用訊息佇列(比如商品有一萬件,就放一萬個請求進來,當然要做好每秒幾個的限制,不能一秒內全放進來,都成功了就繼續放下一批,沒成功就剩下的請求全部返回失敗), 6.讀請求用redis叢集頂住(一個redis也只能頂住幾萬的併發,叫上兄弟) 7.記住一定一定要先把資料庫裡的東西提前載入到redis來,別等使用者查了再加
2.連結暴露的解決方案:
1.有人會說,在上面已經做好了請求X秒一條的限制了嘛,為什麼還要防止連結暴露?
其實在秒殺沒開始前,這個秒殺的介面也是存在的,如果這個介面的url被人知道了,他直接可以在秒殺開始前就通過請求傳輸必要的引數來進行秒殺。我們要做的就是在秒殺開始前,誰都不知道秒殺(也就是付款、減少庫存的介面)這個介面的url是什麼。
2.如何防止秒殺的url暴露?
我們要做的是,在秒殺時間到的時候,才能獲得url。而且秒殺場景中,肯定會有一個倒計時的模組,來告訴你還有幾秒開始秒殺。我們邏輯如下:
①頁面中有一個計時模組,是訪問秒殺頁面的時候去從伺服器裡拿的,計時結束,顯示秒殺的按鈕。 問題:(為什麼不直接取使用者的時間?使用者的本機時間是可以修改的。) ②點選秒殺按鈕後,再次請求伺服器時間,與秒殺的時間對比,如果秒殺進行中,返回一個由加密過的秒殺url 問題:(為什麼還要再次請求伺服器時間?怎麼加密url?,避免時間誤差,md5加密) ③通過這個加密過的url來進行支付、減庫存操作。
3.超賣問題的解決方案:
我們假設現在商品只剩下一件了,此時資料庫中 num = 1;
但有100個執行緒同時讀取到了這個 num = 1,所以100個執行緒都開始減庫存了。
每一個使用者執行緒進來,key值就減1,等減到0的時候,全部拒絕剩下的請求.所以一定不會出現超賣的現象
4.惡意請求的解決方案:
1.怎麼限制讓一個人只能秒殺一件商品?
秒殺成功,將使用者id存入redis集合。通過集合來判斷
2.如果一個人用指令碼掌握了多個賬號去執行秒殺,怎麼辦?
可以讓使用者付款的時候回答問題,防止指令碼的操作。比如12306買火車票的時候,是不是會有按順序點選圖中相同的文字?這就是為了防止指令碼。
5.資料庫層面的解決方案:
1.用訊息佇列來削峰
比如一秒鐘進來1W個寫請求,我們資料庫只能頂住一秒5000個,那我們就每秒放出來4000個。
2.用快取來頂住大量的查詢請求
引入redis,如果redis中有要查詢的資料,就直接返回,如果沒有,從資料庫查詢的時候,把查詢的結果放到redis中,以後查詢都會落到redis層。
在這裡又會出現引用redis常見的問題
問題①:一開始沒有這個key,需要第一次查詢才會到redis,此時秒殺開始,又是一堆併發進來把資料庫打掛了,活動失敗,GG。
解決方案:在活動沒開始前,就把可能會訪問到的資料都載入到redis中,雖然解決方法很簡單,但是一開始沒想到這個請況,會是很嚴重的問題。
問題②:如果這個資料特別熱,突然這個key過期了,然後一堆併發請求過來查詢這個資料,還是把資料庫打掛了。
解決方案: 這也是我們常說的快取擊穿的問題,先看下這個問題的解決方案
1.設定這個key不過期(比如淘寶首頁,只需要在有修改的時候去更新這個key就行)
2.資料庫的查詢時使用互斥鎖,這個時候只有一個執行緒來查詢,不會有一瞬間大量的查詢sql,查詢之後也放在了redis中,後面的查詢就不會打到sql。(查詢的互斥鎖會把查詢的結果集加入互斥鎖,其他的不受影響,也就是說大大降低了相同查詢的併發量)
引入redis中還會出現其他的問題,比如快取雪崩、快取穿透,想更深入瞭解redis
選擇了IT,必定終身學習