前言
本文結構很簡單:
5張圖送你5種秒殺系統,再加點騷操作,再順帶些點心裡話?♀️。
一個簡單的秒殺系統
實現原理: 通過redis原子操作減庫存
圖一
優點 | 缺點 |
---|---|
簡單好用 | 考驗redis服務能力 |
是否公平 |
---|
公平 |
先到先得 |
我們稱這類秒殺系統為:
簡單秒殺系統
如果剛開始QPS並不高,redis完全抗的下來的情況,完全可以依賴這個「簡單秒殺系統」。
一個夠用的秒殺系統
實現原理: 服務記憶體限流演算法 + redis原子操作減庫存
圖二
優點 | 缺點 |
---|---|
簡單好用 | - |
是否公平 |
---|
不是很公平 |
相對的先到先得 |
我們稱這類秒殺系統為:
夠用秒殺系統
效能再好點的秒殺系統
實現原理: 服務本地記憶體原子操作減庫存
服務本地記憶體的庫存怎麼來的?
活動開始前分配好每臺機器的庫存,推送到機器上。
圖三
優點 | 缺點 |
---|---|
高效能 | 不支援動態伸縮容(活動進行期間),因為庫存是活動開始前分配好的 |
釋放redis壓力 | - |
是否公平 |
---|
不是很公平 |
不是絕對的先到先得 |
我們稱這類秒殺系統為:
預備庫存秒殺系統
支援動態伸縮容的秒殺系統
實現原理: 服務本地協程Coroutine定時redis原子操作減部分庫存到本地記憶體 + 服務本地記憶體原子操作減庫存
圖四
優點 | 缺點 |
---|---|
高效能 | 支援動態伸縮容(活動進行期間) |
釋放redis壓力 | - |
具備通用性 | - |
是否公平 |
---|
不是很公平,但是好了點 |
幾乎先到先得 |
我們稱這類秒殺系統為:
實時預備庫存秒殺系統
公平的秒殺系統
實現原理: 服務本地Goroutine定時同步是否售罄到本地記憶體 + 佇列 + 排隊成功輪訓(或主動Push)結果
圖五
優點 | 缺點 |
---|---|
高效能 | 開發成本高(需主動通知或輪訓排隊結果) |
真公平 | - |
具備通用性 | - |
是否公平 |
---|
很公平 |
絕對的先到先得 |
我們稱這類秒殺系統為:
公平排隊秒殺系統
騷操作
上面的秒殺系統還不夠完美嗎?
答案:是的。
還有什麼優化的空間?
答案:靜態化獲取秒殺活動資訊的介面。
靜態化是什麼意思?
答案:比如獲取秒殺活動資訊是通過介面 https://seckill.skrshop.tech/v1/acticity/get
獲取的。現在呢,我們需要通過https://static-api.skrshop.tech/seckill/v1/acticity/get
這個介面獲取。有什麼區別呢?看下面:
服務名 | 介面 | 資料儲存位置 |
---|---|---|
秒殺服務 | https://seckill.skrshop.tech/... | 秒殺服務記憶體或redis等 |
介面靜態化服務 | https://static-api.skrshop.te... | CDN、本地檔案 |
以前是這樣
變成了這樣
結果:可以通過介面https://static-api.skrshop.tech/seckill/v1/acticity/get
就獲取到了秒殺活動資訊,流量都分攤到了cdn,秒殺服務自身沒了這部分的負載。
小聲點說:“秒殺結果我也敢推CDN???。”
備註:
之後我們會分享`如何用Golang設計一個好用的「介面靜態化服務」`。
總結
上面我們得到了如下幾類秒殺系統
秒殺系統 |
---|
簡單秒殺系統 |
夠用秒殺系統 |
預備庫存秒殺系統 |
實時預備庫存秒殺系統 |
公平排隊秒殺系統 |
我想說的是裡面沒有最好的方案,也沒有最壞的方案,只有適合你的。
拿先到先得
來說,一定要看你們的產品對外宣傳,切勿上來就追逐絕對的先到先得。其實你看所有的方案,相對而言都是“先到先得”,比如,活動開始一個小時了你再來搶,那相對於準時的使用者自然搶不過,對吧。
又如預備庫存秒殺系統
,雖然不支援動態伸縮容。但是如果你的環境滿足如下任意條件,就完全夠用了。
-
秒殺場景結束時間之快,通常幾秒就結束了,真實活動可能會發生如下情況:
- 服務壓力大還沒掛:根本就來不及動態伸縮容
- 服務壓力大已經掛了:可以先暫停活動,服務起來&擴容結束,用剩餘庫存重新推送
- 運維自身不具備動態伸縮容的能力
所以:
合適好用就行,切勿過度設計。
最後
這次算是把老本都吐露出來了,真是慌得一匹。
SkrShop歷史分享:https://github.com/skr-shop/m...