來源:t.cn/EAlQqQDjava
秒殺活動是絕大部分電商選擇的低價促銷,推廣品牌的方式。既能夠給平臺帶來用戶量,還能夠提升平臺知名度。一個好的秒殺系統,能夠提升平臺系統的穩定性和公平性,得到更好的用戶體驗,提高平臺的口碑,從而提高秒殺活動的最大價值。redis
本文討論雲數據庫Redis版緩存設計高併發的秒殺系統。數據庫
秒殺活動對稀少或特價的商品進行定時定量售賣,吸引成大量的消費者進行搶購,但又只有少部分消費者能夠下單成功。所以,秒殺活動將在必定時間內產生比平時大幾十倍倍,上百倍的頁面訪問流量和下單請求流量。瀏覽器
秒殺活動能夠分爲3個階段:緩存
消費者提交的訂單,通常作法是利用數據庫的行級鎖,只有搶到鎖的請求能夠進行庫存查詢和下單操做。可是在高併發的狀況下,數據庫沒法承受如此大的請求,每每須要整個服務被阻止,在消費者看來就是服務器停機機。服務器
利用系統的層次結構,在每一個階段提早從新驗證,攔截無效流量,能夠減小大量無效的流量涌入數據庫。網絡
利用瀏覽器緩存和CDN抗壓靜態頁面流量數據結構
所以,咱們須要把秒殺商品詳情頁與普通的商品詳情頁分開。關於秒殺商品詳情頁試圖將能靜態化的元素靜態化處理,除了秒殺按鈕須要服務端進行動態判斷,其餘的靜態數據能夠緩存在瀏覽器和CDN上。這樣,秒殺前刷新頁面致使的流量進入服務端的流量只有很小的一部分。併發
利用識讀分離Redis緩存攔截流量app
CDN是第一級流量攔截,第二級流量攔截咱們使用支持讀寫分離的Redis。在這一階段咱們主要讀取數據,讀取分離Redis能支持高達60萬以上qps,徹底能夠支持需求。
首先經過數據控制模塊,提早將秒殺商品緩存到標識符分離Redis,並設置秒殺開始標記以下:
"goodsId_count": 100 //總數
"goodsId_start": 0 //開始標記
"goodsId_access": 0 //接受下單數
秒殺開始前,服務從新讀取goodsId_Start爲0,直接返回未開始。
數據控制模塊將goodsId_start改成1,標誌秒殺開始。
服務最大化緩存開始標記位並開始接受請求,並記錄到redis中goodsId_access,商品剩餘數量爲(goodsId_count-goodsId_access)。
當接受下單數達到goodsId_count後,繼續攔截全部請求,商品剩餘數量爲0。
能夠拋光,最後成功參與下單的請求只有少部分能夠被接受。在高併發的狀況下,容許稍微多的流量進入。所以能夠控制接受下單數的比例。
利用主從版Redis緩存加速庫存扣量
成功避免下單後,進入下層服務,開始進行訂單信息校驗,庫存扣量。爲了不直接訪問數據庫,咱們使用主從版Redis來進行庫存扣量,主從版Redis提供10萬等級的QPS。使用Redis來優化庫存查詢,提早攔截秒殺失敗的請求,將大大提升系統的總體穩定性。
經過數據控制模塊提早將庫存存入Redis,將每一個秒殺商品在Redis中用一個hash結構表示。
"goodsId" : {
"Total": 100
"Booked": 100
}
扣量時,服務器經過請求Redis獲取下單資格,經過如下lua腳本實現,經過Redis是單線程模型,lua能夠保證多個命令的原子性。
local n = tonumber(ARGV[1])
if not n or n == 0 then
return 0
end
local vals = redis.call("HMGET", KEYS[1], "Total", "Booked");
local total = tonumber(vals[1])
local blocked = tonumber(vals[2])
if not total or not blocked then
return 0
end
if blocked + n <= total then
redis.call("HINCRBY", KEYS[1], "Booked", n)
return n;
end
return 0
先使用SCRIPT LOAD
將lua腳本EVALSHA
預先緩存在Redis,而後調用調用腳本,比直接調用EVAL
節省網絡帶寬:
redis 127.0.0.1:6379>SCRIPT LOAD "lua code"
"438dd755f3fe0d32771753eb57f075b18fed7716"
redis 127.0.0.1:6379>EVAL 438dd755f3fe0d32771753eb57f075b18fed7716 1 goodsId 1
秒殺服務經過判斷Redis是否返回搶購個數n,便可知道這次請求是否扣量成功。
使用主從版Redis實現簡單的消息異步下單入庫
若是商品數量減小的時候,直接操做數據庫便可。若是秒殺的商品是1萬,甚至10萬等級,那數據庫鎖衝突將帶來很大的性能優點。。所以,利用消息組件,當秒殺服務將訂單信息寫入消息變量後,便可認爲下單完成,避免直接操做數據庫。
```java
orderList {
[0] = {訂單內容}
[1] = {訂單內容}
[2] = {訂單內容}
...
}
```java
LPUSH orderList {訂單內容}
```java
BRPOP orderList 0
經過使用Redis做爲消息收發器,異步處理訂單入庫,有效的提升了用戶的下單完成速度。
數據控制模塊管理秒殺數據同步
最開始,利用識別分離Redis進行流量限制,只讓部分流量進入下單。對於下單檢驗失敗和退單等狀況,須要讓更多的流量進來。所以,數據控制模塊須要定時將數據庫中的數據進行必定的計算,同步到主從版Redis,同時再同步到讀寫分離的Redis,讓更多的流量進來。