當前位置:
首頁 > 知識 > 阿里雲Redis讀寫分離典型場景:如何輕鬆搭建電商秒殺系統

阿里雲Redis讀寫分離典型場景:如何輕鬆搭建電商秒殺系統

秒殺活動是絕大部分電商選擇的低價促銷,推廣品牌的方式。不僅可以給平台帶來用戶量,還可以提高平台知名度。一個好的秒殺系統,可以提高平台系統的穩定性和公平性,獲得更好的用戶體驗,提昇平台的口碑,從而提升秒殺活動的最大價值。

本次主要討論阿里云云資料庫Redis緩存設計高並發的秒殺系統。


秒殺的特徵

秒殺活動對稀缺或者特價的商品進行定時,定量售賣,吸引成大量的消費者進行搶購,但又只有少部分消費者可以下單成功。因此,秒殺活動將在較短時間內產生比平時大數十倍,上百倍的頁面訪問流量和下單請求流量。

秒殺活動可以分為3個階段:

  • 秒殺前:用戶不斷刷新商品詳情頁,頁面請求達到瞬時峰值。

  • 秒殺開始:用戶點擊秒殺按鈕,下單請求達到瞬時峰值。

  • 秒殺後:一部分成功下單的用戶不斷刷新訂單或者產生退單操作,大部分用戶繼續刷新商品詳情頁等待退單機會。

消費者提交訂單,一般做法是利用資料庫的行級鎖。只有搶到鎖的請求可以進行庫存查詢和下單操作。但是在高並發的情況下,資料庫無法承擔如此大的請求,往往會使整個服務blocked,在消費者看來就是伺服器宕機。


秒殺系統

系統架構圖

阿里雲Redis讀寫分離典型場景:如何輕鬆搭建電商秒殺系統

秒殺系統的流量雖然很高,但是實際有效流量是十分有限的。利用系統的層次結構,在每個階段提前校驗,攔截無效流量,可以減少大量無效的流量湧入資料庫。

利用瀏覽器緩存和CDN抗壓靜態頁面流量

秒殺前,用戶不斷刷新商品詳情頁,造成大量的頁面請求。所以,我們需要把秒殺商品詳情頁與普通的商品詳情頁分開。對於秒殺商品詳情頁盡量將能靜態化的元素盡量靜態化處理,除了秒殺按鈕需要服務端進行動態判斷,其他的靜態數據可以緩存在瀏覽器和CDN上。這樣,秒殺前刷新頁面導致的流量進入服務段的流量只有很小的一部分

利用阿里雲讀寫分離Redis緩存攔截流量

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可以保證多個命令的原子性:

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腳本提前緩存在Redis,然後調用EVALSHA調用腳本,比直接調用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萬級別,那資料庫鎖衝突將帶來很大的性能瓶頸。因此,利用消息隊列組件,當秒殺服務將訂單信息寫入消息隊列後,即可認為下單完成,避免直接操作資料庫。

消息隊列組件依然可以使用Redis實現,在R2中用list數據結構表示:

orderList {
[0] = {訂單內容}
[1] = {訂單內容}
[2] = {訂單內容}
...
}

將訂單內容寫入Redis:

LPUSH orderList {訂單內容}

非同步下單模塊從Redis中順序獲取訂單信息,並將訂單寫入資料庫:

BRPOP orderList 0

我們通過使用Redis作為消息隊列,非同步處理訂單入庫,有效的提高了用戶的下單完成速度。

數據控制模塊,管理秒殺數據同步

最開始,我們利用阿里雲讀寫分離Redis進行流量限制,只讓部分流量進入下單。對於下單檢驗失敗和退單等情況,我們需要讓更多的流量進來。因此,數據控制模塊需要定時將資料庫中的數據進行一定的計算,同步到主從版Redis,同時再同步到讀寫分離的Redis,讓更多的流量進來。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雲棲社區 的精彩文章:

PostgreSQL 用 CTE語法 + 繼承 實現平滑拆分大表
網站加速新時代 阿里雲CDN推出全站加速服務

TAG:雲棲社區 |