学习目的:
- 在商品抢购的时候怎么解决线程安全问题?
- 分布式锁的基本原理是什么?
- 怎么杜绝redis使用时死锁问题?
- 如何解决redis分布式锁中的锁失效问题?
- redis分布式锁中,刚创建一个key,还没来得及主从复制,redis宕机了,怎么解决数据不一致问题?
- 如何将redis效率成倍提升?
用例代码:
//spring内部提供的 模板类
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequetMapping("/reduceProduce")
public String reduceProduce() throws Exception {
// 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));
if (produceNum > 0) {
int remainProduceNum = produceNum - 1;
// 库存数量减一 相当于操作 : jedis. set (key, value)
stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
} else {
System.out.println("扣减失败,库存不足");
}
return "end";
}
在商品抢购的时候怎么解决线程安全问题?
让我们想一想上面用例代码存在什么问题?
是的。存在线程安全问题,当多个用户进行访问的时候,或造成重复扣除库存的问题
怎么解决呢?让我们看一下接下来的代码用例
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequetMapping("/reduceProduce")
public String reduceProduce() throws Exception {
synchronized (this) {
// 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));
if (produceNum > 0) {
int remainProduceNum = produceNum - 1;
// 库存数量减一 相当于操作 : jedis. set (key, value)
stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
} else {
System.out.println("扣减失败,库存不足");
}
}
return "end";
}
于是我们想到了使用synchronized关键字,给其进行加锁。
但是上面代码只适合在单体架构中使用。当你是分布式架构和微服务架构的时候,要怎么解决呢。每一个项目运行时是作用于自己独立的jvm中,每个项目中的synchronized只作用于自己的jvm中(又回到了上述的线程安全问题了)。所以如果你是单体架构放心使用进行,如果你是分布式架构或者微服务架构,就要引入接下来的redis分布式架构了。 接下来让我们了解一下redis分布式锁的运行原理
分布式锁的基本原理
正常redis的时候 set key value
redis锁使用 setnx key value
区别:set key value。当你第二次进行set相同key会将值进行覆盖,无论如何都会进行减库存操作。
setnx key value。当你第二次进行set相同key会判断该key是否存在,如果存在就不进行减库存操作,如果不存在就进行减库存操作。
运用分布式锁修改后的代码
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequetMapping("/reduceProduce")
public String reduceProduce() throws Exception {
try{
//相当于执行了 jedis. setnx 的指令 执行成功返回true,执行失败返回false
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("produceNum","库存数量");
if (!result){
return "活动太火爆,请稍后重试";
}
// 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));
if (produceNum > 0) {
int remainProduceNum = produceNum - 1;
// 库存数量减一 相当于操作 : jedis. set (key, value)
stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
} else {
System.out.println("扣减失败,库存不足");
}
}finally{
//释放锁
stringRedisTemplate.delete("produceNum");
}
return "end";
}
怎么杜绝redis使用时死锁问题
上面代码如果在执行 if (produceNum > 0) {} 这一行代码的时候,服务突然挂了,finally中释放锁的操作还未进行,当你再次进行访问的时候,该key的商品还是存在的,无论如何都是访问不到的,这个时候就会造成死锁问题。如果解决呢?让我们看看接下来的代码。
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequetMapping("/reduceProduce")
public String reduceProduce() throws Exception {
try {
//相当于执行了 jedis. setnx 的指令 执行成功返回true,执行失败返回false 设置过期时间
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent ("produceNum", "库存数量", 10, TimeUnit. SECONDS);
if (!result){
return "活动太火爆,请稍后重试";
}
// 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));
if (produceNum > 0) {
int remainProduceNum = produceNum - 1;
// 库存数量减一 相当于操作 : jedis. set (key, value)
stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
} else {
System.out.println("扣减失败,库存不足");
}
}finally {
//释放锁操作
stringRedisTemplate.delete("produceNum");
}
return "end";
}
是的,给其添加一个过期时间即可。所以在使用redis的时候,一定要注意给其添加一个过期时间和进行释放锁的操作。
如何解决redis分布式锁中的锁失效问题?
可能造成锁失效的情况
- 在事物内务使用锁,锁在事物提交前释放
解决方案:在上锁之前,在事物的外层上锁。(新建一个方法,调用事物方法)
- 业务未执行完,锁提前释放
例如:上锁10秒钟,业务流程执行15秒,就可能造成锁失效的问题
解决方案:1.将锁的过期时间设置的长一点 2.使用redisson的watchdog机制给锁续命,给锁一个uuId的标识,没等过期时间进行三分之一进行.通过uuid进行判断是否是自己的锁进行续命。
redis分布式锁如何保证数据的一致性
redis分布式锁会造成数据缺失,但是效率很高。
所以 如果能允许少量数据的缺失保证高的效率使用redis分布式锁。如果不允许少量数据的缺失,可以使用zookeeper分布式锁。
如何将redis效率成倍提升?
这里可以使用分段锁提升redis的效率。
锁续命,给锁一个uuId的标识,没等过期时间进行三分之一进行.通过uuid进行判断是否是自己的锁进行续命。
redis分布式锁如何保证数据的一致性
redis分布式锁会造成数据缺失,但是效率很高。
所以 如果能允许少量数据的缺失保证高的效率使用redis分布式锁。如果不允许少量数据的缺失,可以使用zookeeper分布式锁。
如何将redis效率成倍提升?
这里可以使用分段锁提升redis的效率。
分段锁:之前存商品 100个 ,采用分段锁后是 key1 10; key2 10; key3 10;… key10 10;配合lua脚本使用。