使用Redis搭建電商秒殺系統

2022-09-15 05:21:13 字數 3110 閱讀 3057

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

本文討論雲資料庫redis版快取設計高併發的秒殺系統。

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

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

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

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

利用瀏覽器快取和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" :
扣量時,伺服器通過請求redis獲取下單資格,通過以下lua指令碼實現,由於redis是單執行緒模型,lua可以保證多個命令的原子性。

local n = tonumber(ar**[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  

[1] =

[2] =

...}

2.將訂單內容寫入redis:

lpush orderlist
3.非同步下單模組從redis中順序獲取訂單資訊,並將訂單寫入資料庫。

brpop orderlist 0
通過使用redis作為訊息佇列,非同步處理訂單入庫,有效的提高了使用者的下單完成速度。

資料控制模組管理秒殺資料同步

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

電商秒殺系統設計

秒殺場景一般會在電商 舉行一些活動或者節假日在12306 上搶票時遇到。對於電商 中一些稀缺或者 商品,電商 一般會在約定時間點對其進行限量銷售,因為這些商品的特殊性,會吸引大量使用者前來搶購,並且會在約定的時間點同時在秒殺頁面進行搶購。限流 鑑於只有少部分使用者能夠秒殺成功,所以要限制大部分流量,...

電商秒殺優化

增加並行數量,就是增大對資料庫的訪問。而這三種優化快取效果排序 頁面快取 url快取 物件快取 jss,js這些內容的優化 首先在goodscontroller中找到商品列表goodlist,資料通過model來傳到good list.html頁面中去 那麼如何取出我們的頁面快取呢?通過下面這句 s...

電商秒殺專案 秒殺模組

itemmodel中新增乙個 private promomodel promomodel 並建立get set方法。修改getitembyid方法 override public itemmodel getitembyid integer id itemmodel itemmodel convert...