我是靠谱客的博主 开朗过客,这篇文章主要介绍RedisTemplate解决高并发下秒杀系统库存超卖方案 — Redis实现分布式锁机制,现在分享给大家,希望可以做个参考。

1、场景

秒杀系统存在高并发的场景,在对商品进行秒杀时,由于并发过高可能会导致库存超卖的情况,那么可以通过Redis提供的事务机制超卖问题;通过Redis提供的SetExNx机制实现上锁一致性,利用lua脚本语句,实现解锁一致性,而从解决超卖问题;


加锁原子性:通过redis自身的setnxex命令即可,setIfAbsent(“lockKey”, value, timeOut, TimeUnit);


解锁原子性:通过redis+lua脚本实现;


2、复现超卖场景

2.1 初始化库存接口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController @RequestMapping("/redis") @Slf4j public class RedisController { @Resource     private RedisTemplate redisTemplate; //记录实际卖出的商品数量     private AtomicInteger successNum = new AtomicInteger(0); @GetMapping(value = "/init")     public String init() {         // 初始化库存数量,模拟库存只要5个商品,写入到redis中         redisTemplate.opsForValue().set("stock", 5); successNum.set(0);         log.info("===>>>库存初始化成功,库存数为" + 5);         return "初始化库存成功";     } }

2.2 库存扣减接口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RestController @RequestMapping("/redis") @Slf4j public class RedisController { @Resource     private RedisTemplate redisTemplate; //记录实际卖出的商品数量     private AtomicInteger successNum = new AtomicInteger(0);      @GetMapping(value = "/reduce")     public String reduce() {         int stock = (Integer) redisTemplate.opsForValue().get("stock");         log.info("===>>>当前数量" + stock);         // 模拟只减少一个库存         stock = stock - 1;         if (stock < 0) {             log.info("===>>>库存不足");             return "库存不足";         }         // 将剩余数量回写到redis         redisTemplate.opsForValue().set("stock", stock);         // 记录实际卖出的商品数量(线程安全每个请求都会记录)         log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());         return "减少库存成功";     } }

2.3 测试

使用工具JMeter模拟并发请求,此处模拟每秒200次;

注意:测试前先执行初始化库存接口,保证库存写入到Redis中

使用JMeter请求接口,结果如下图:


3、解决超卖实现

3.1 初始化库存接口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController @RequestMapping("/redis") @Slf4j public class RedisController { @Resource     private RedisTemplate redisTemplate; //记录实际卖出的商品数量     private AtomicInteger successNum = new AtomicInteger(0);      @GetMapping(value = "/init")     public String init() {         // 初始化库存数量,模拟库存只要5个商品,写入到redis中         redisTemplate.opsForValue().set("stock", 5); successNum.set(0);         log.info("===>>>库存初始化成功,库存数为" + 5);         return "初始化库存成功";     } }

3.2 库存扣减接口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@RestController @RequestMapping("/redis") @Slf4j public class RedisController { @Resource     private RedisTemplate redisTemplate; //记录实际卖出的商品数量     private AtomicInteger successNum = new AtomicInteger(0);      @GetMapping(value = "/reduce")     public String reduce() {         int stock = (Integer) redisTemplate.opsForValue().get("stock");         if (stock <= 0) {             log.info("===>>>库存不足");             return "库存不足";         }         String LOCK_KEY = "lockKey";         String value = UUID.randomUUID().toString();         // value值任意即可,秒杀设置锁的时间为1秒(根据实际情况更多)         boolean absent = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, value, 1, TimeUnit.SECONDS);         if (absent) {             // 当前key没有锁,加锁成功             log.info("===>>>加锁成功,获取并扣减库存");             Integer sku = (Integer) redisTemplate.opsForValue().get("stock");             //模拟只减少一个库存             sku = sku - 1;             if (sku < 0) {                 log.info("===>>>库存不足");                 // 执行脚本 删除锁                 redisLockServer.deleteLock(LOCK_KEY, value);                 return "库存不足";             }             // 将扣减后的数量写入redis             redisTemplate.opsForValue().set("stock", sku);             log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());             // 执行脚本 删除锁             List<String> lockKeys = Collections.singletonList(LOCK_KEY);             String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return 1 else return 0 end";             RedisScript<Long> luaScript = RedisScript.of(lua, Long.class);             // 删除锁             Object execute = redisTemplate.execute(luaScript, lockKeys, value);             log.info("===>>>抢购成功");             return "抢购成功";         } else {             return "抢购失败";         }     } }

3.3 测试

使用工具JMeter模拟并发请求,此处模拟每秒200次;

注意:测试前先执行初始化库存接口,保证库存写入到Redis中

使用JMeter请求接口,结果如下图,没有出现超卖情况:

在这里插入图片描述

结论
通过Redis分布式锁机制,能够有效的解决秒杀系统的超卖问题;

其他实现方式: Redis事务+乐观锁



最后

以上就是开朗过客最近收集整理的关于RedisTemplate解决高并发下秒杀系统库存超卖方案 — Redis实现分布式锁机制的全部内容,更多相关RedisTemplate解决高并发下秒杀系统库存超卖方案内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(342)

评论列表共有 0 条评论

立即
投稿
返回
顶部