秒殺業務的基礎點

秒殺的是一個很常見的業務了。就是在某個時刻,讓大量用戶搶購少量的優惠的商品,從而達到商品曝光和電商網站的曝光,增大用戶流量,從而提升整體銷售額。

比如今年疫情下,各大電商網站,就針對口罩開展了秒殺業務。

通用秒殺邏輯

1、秒殺頁動態獲取服務器時間,前端根據時間倒計時
2、倒計時結束,開始秒殺
3、獲取後端秒殺接口
4、真正執行秒殺(減庫存,下訂單insert)
一般減庫存是放在redis中頂的,因爲秒殺那一刻,會有大量請求過來。

下單秒殺

說說之前的一種秒殺場景,就是下單秒殺。
就是每個人都可以下單,只有你下單成功了,然後減庫存成功了,纔算秒殺成功。
這種秒殺,相對上面那種,會比較重一些,比較難一點。
這種情況,就必不可少的會用數據庫。
做的複雜點,是將所有的操作先在nosql中保存起來,或者發出一個mq消息,然後讓數據庫慢慢消化,但這樣就可能會出現不一致的問題。
所以,如果用戶量不是特別大,也可以直接考慮用數據庫頂。
如果用數據庫頂,也要考慮,比如先下單,再減庫存好,還是先減庫存,再下單好。
直接說結果,那就是先下單,再減庫存比較好。

以 MySQL 作爲 DB 爲例,下訂單就是 insert,在使用索引的情況下,insert 插入是行級鎖,支持每秒 4W 的併發。減庫存就是 update 操作,命中索引時也是行級鎖,但是這是個獨佔鎖,所有的操作都要等待前一個釋放鎖後才能繼續 update。

問題就在這裏,根據 MySQL 兩段鎖協議,我們應該把熱點操作放到離 commit 近的位置,這樣可以減少行級鎖的持有時間!自然處理效率就更好一些。

購買資格秒殺

比如京東的那個賣口罩的秒殺,秒殺成功只是代表你獲得了購買的資格,然後你跳到結算頁,就可以購買成功了,而沒有獲取秒殺資格的人,短時間也可以跳到結算頁,最終會購買失敗。如果庫存減完了,頁面直接置灰,顯示秒殺結束。

那這個實現方式,相比秒殺完還要下單的場景,就簡單多了,redis搞一個原子計數器,設置好庫存,秒殺成功減庫存,同時記錄秒殺成功的用戶。後面只有這些記錄的用戶纔可以購買。

mysql

如果你是一個小的商城服務,成本有限,沒有時間和錢去維護那麼多nosql,mq等高可用服務,實在沒辦法了就直接用mysql先抗吧,畢竟小商城,既要有秒殺這種活動,又不想多花錢,但好在用戶量也不大,所以用mysql,我覺得用的好的話,也能抗住不少。

比如可以寫存儲過程,將insert和update放在一個存儲過程中,這樣的好處是可以減少java客戶端與mysql的執行次數,最終是減少了事務鎖的時間。
但存儲過程,我並沒有在真正的生產環境見過這樣用的,甚至一些大併發的部門,都是禁止用存儲過程的,所以這個用存儲過程,也只是一種民間說法,可以作爲一個思考方向吧。

緩存

cdn緩存

一般秒殺頁的靜態資源,包括一些基本不變的css,js都可以靜態化處理推送在cdn緩存上。

redis緩存

redis緩存,爲了擋住大部分到數據庫的請求。
緩存和數據庫的一致性考慮,需根據業務不同自行判斷。
對於幾乎不可改的數據,可以用redis的過期時間,過期了就再從數據庫查一次。
對於常改的數據,就需要每次改完數據庫都同步一下redis了。
普通業務,只要產品經理靠譜點,都會搞成不可改的數據,如果錯了就廢棄掉,新建一條。

序列化

redis由於不能存對象,一般可以存json,或者byte數組。
一般使用java原生的序列化也行,就存byte數組,或者糙一點,存json字符串也有,但講究點的,可以選擇換種序列化方式。
這裏個人推薦用protobuf序列化。
protostuff序列化包,使用protobuf序列化協議,相比java原生的序列化,速度快,佔用空間還小。
爲什麼又快又小,可參考:https://github.com/eishay/jvm-serializers/wiki

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章