#Redis在資源秒殺場景中的使用git
時間軸 | 業務 | 流程節點備註 |
---|---|---|
週一 | 生成資源數據 | 流程① |
週三10:00前 | check資源數據 | 流程② |
週三10:00 | 購買者秒殺秒殺資源 | 流程③ |
週三10:00後 | 購買者退款 | 流程④ |
週日 | 本週資源搶購結束,生成外網展現信息 | 流程⑤ |
緩存redis-數據熱身 流程①② (牛奶供給降級策略)github
關鍵僞代碼redis
cacheRedis.setex(key,EXPIRE_TIME_7D,info);
複製代碼
秒殺qps峯值在1w左右,可是超過60%的qps請求的是查詢列表方法,因此須要增長可購買秒殺資源緩存。spring
關鍵僞代碼數據庫
生成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-秒殺資源秒殺 流程③bash
每一個秒殺資源擁有本身的隊列,完成多隊列,低隊列長度的秒殺。服務器
關鍵僞代碼架構
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秒殺資源隊列的訂單,其餘有資格購買的購買者能夠繼續購買。異步