我的博客大纲
我的后端学习大纲
1、问题分析:
1.1.问题:
1.2.场景:
- 1.如果业务逻辑的执行时间是7s。执行流程如下
- 1.index1业务逻辑没执行完,3秒后锁被自动释放。
- 2.index2获取到锁,执行业务逻辑,3秒后锁被自动释放。
- 3.index3获取到锁,执行业务逻辑
- 4.index1业务逻辑执行完成,开始调用del释放锁,这时释放的是index3的锁,导致index3的业务只执行1s就被别人释放。最终等于没锁的情况
1.3.解决方式:
- 1.setnx获取锁时,设置指定一个
的唯一值(例如:uuid)
;释放前获取这个值,判断是否自己的锁
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e4f479a4dad19910b4ee5e6f8fa54ce8.png)
1.4.编码实现:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b77973c1f9bada4e58fb95732ba7aa6b.png)
2.新问题改善:
2.1.新问题说明:
- 1.上述改善后,出现的新问题就是判断与删除条件不再一个命令中,操作缺乏原子性
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6eeadc7071b541eabb688c8d9048d5e2.png)
2.2.场景:
- 1.index1执行删除时,查询到的lock值确实和uuid相等
- 2.index1执行删除前,lock刚好过期时间已到,被redis自动释放
- 3.index2获取了lock
- 4.index1执行删除,此时会把index2的lock删除
2.3.解决方案:
- 在redis中没有一个命令可以同时做到判断 + 删除,所有只能通过其他方式实现
(如LUA脚本实现)
2.4.LUA脚本解决:
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
public void deduct() {
String uuid = UUID.randomUUID().toString();
while (!this.redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS)) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
String stock = redisTemplate.opsForValue().get("stock").toString();
if (stock != null && stock.length() != 0) {
Integer st = Integer.valueOf(stock);
if (st > 0) {
redisTemplate.opsForValue().set("stock", String.valueOf(--st));
}
}
} finally {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
"then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList("lock"), uuid);
}
}
2.5.压力测试:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/09e4fa950d96422e91f96cd371d5f474.png)