#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秒杀资源队列的订单,其余有资格购买的购买者能够继续购买。异步