回顧目錄
- 業務場景 | 同步秒殺實現:Redis在秒殺功能的實踐
- 設計模式 | 一句話理解設計模式
- Apache SkyWalking | SkyWalking之高階玩法
- Apache SkyWalking | SkyWalking Java 外掛貢獻實踐
- 分散式作業排程 | 鏈家分散式作業平臺(作者推薦)
Redis在資源秒殺場景中的使用
業務概述
- 秒殺資源:以周為時長的資源。
- 每個頁面都會有秒殺資源,數量在1~8份,以隨機形式展示給訪客。
- 每週秒殺資源價格由資料部門計算定價,沒有有一個時間點進行搶購,如:每週三10點。購買者搶購數量可以是 秒殺資源剩餘資源中的任意數量。
- 購買者是否有搶購秒殺資源的許可權,由使用者介面資訊,賬戶資訊,等許可權介面等決定。
- 購買者支付方式使用介面支付,系統生成購買者搶購支付加密資訊,跳轉支付頁面,再支付介面後,非同步回掉確定是否購買成功,如果購買失敗需要及時退回秒殺資源庫存,以供他人報買。
業務流程
時間軸 | 業務 | 流程節點備註 |
---|---|---|
週一 | 生成資源資料 | 流程① |
週三10:00前 | check資源資料 | 流程② |
週三10:00 | 購買者秒殺秒殺資源 | 流程③ |
週三10:00後 | 購買者退款 | 流程④ |
週日 | 本週資源搶購結束,生成外網展示資訊 | 流程⑤ |
Redis節點說明
- 通用redis:用於SSO做統一登入、以及非秒殺功能使用。
- 快取redis:用於儲存購買者熱身資料,搶購這查詢資訊的快取。
- 核心redis:負責資源庫存剩餘數量,秒殺秒殺資源搶佔等核心業務實現,需要關閉redis的lru策略,程式控制記憶體中key的淘汰
Redis使用詳情
-
快取redis-資料熱身 流程①② (牛奶供給降級策略)
-
關鍵虛擬碼
cacheRedis.setex(key,EXPIRE_TIME_7D,info); 複製程式碼
-
秒殺qps峰值在1w左右,但是超過60%的qps請求的是查詢列表方法,所以需要增加可購買秒殺資源快取。
-
關鍵虛擬碼
生成rediskey, objects包括ucid、使用者輸入入參、分頁資訊等等 public static String builder(String prefix, Object... objects) { String input = JSONObject.toJSONString(Arrays.asList(objects)); String output = Util.md5_16(input); return prefix+output; } cacheRedis.setex(key,EXPIRE_TIME_2S,info); 複製程式碼
-
設計優點:借鑑spring-data-redis將入參通用為objects...序列化,然後將JsonString Md5壓縮為16位,這裡主要由於在秒殺開始時,redis資料會出現大量快取列表資料,redis儲存100w個value長度為32位,key長度為16位的資料時,需要使用個130MB記憶體,如果key的長度為32位時需要160MB左右的記憶體,所以壓縮key的長度在這種場景很有必要。
-
-
核心redis-秒殺資源秒殺 流程③
-
每個秒殺資源擁有自己的佇列,完成多佇列,低佇列長度的秒殺。
-
關鍵虛擬碼
String key = PURCHASING_PRODUCT + productId; Long count = coreRedis.llen(key); 判斷count是否大於庫存 判斷count+使用者欲購買秒殺資源數量(share)是否大於庫存 String[] values = (uuid+uid) * share; if (inventory - coreRedis.lpush(key, values)) < 0) { coreRedis.lrem(key, share, values); } 例如:id:1 秒殺資源有3份流量的庫存, 當llen時發現秒殺資源在redis中沒有資料, 購買者20xxxxx1想買此資源3份流量, 這時lpush後發現超賣,lrem退回庫存。 redis 127.0.0.1:6379> lrange XX_PRODUCT_1 0 -1 1) "jali7xz20xxxxx1" 2) "3whsh6b20xxxxx2" 3) "3whsh6b20xxxxx2" 4) "3whsh6b20xxxxx2" 複製程式碼
-
設計優點:核心命令llen、lpush的時間複雜度都是O(1)、lrem時間複雜度是O(N),官方lrem給出的複雜度是O(N)但我覺得在這種使用場景下lrem的複雜度應該無極限接近於O(count),但是將補償操作封裝為原子性,且支援多次、冪等執行。曾經也想過用一些getset,setnx,pipelin、將庫存快取到佇列然後pop、事務等實現秒殺。但是效能、或者魯棒性在這種場景下都沒有以上設計表現出色,而且這種方式在支付失敗,或者查詢到未支付的情況下立刻冪等lrem秒殺資源佇列的訂單,其他有資格購買的購買者可以繼續購買。
-
Redis線上使用情況
- 快取redis (圖片來源地址:github)
- 核心redis
Redis使用總結
- 使用一主一從,rdb為備份策略的redis架構,QPS在8W以下是沒有任何問題的(第一期秒殺資源秒殺,在沒有做redis多庫負載切分,以及沒有優化使用的情況下到了5W的QPS,沒有出現超時連結,或者獲取不到連線池資源的情況,也和沒有使用事務以及採用的低複雜度命令實現有關)
- 像列表頁快取,切勿為了減少redis的開銷,將資料庫每一列放到redis中,在redis中查詢彙總,例如:每個秒殺資源都放在redis中,秒殺資源頁需要10次redis連結才能完成一次列表頁的組裝。這樣做會將伺服器的qps成幾何倍數的擴大到與redis的qps中造成系統獲取不到redis連線資源。
- 如果redis只用作快取資料,且追求極限效能,master可以關閉記憶體快照和日誌記錄,有slave節點完成。
- 批量命令降低QPS可使用Redisson、Lettuce或lua指令碼代替。