标签:delete red The second 设立 最简 实现 ring 异常
基于Redis实现分布式所得简单逻辑
最简单逻辑:
//注入
StringRedisTemplate stringRedisTemplate
//原理就是利用Redis set k v NX 指令(setIfAbsent方法), NX表示添加k v如果Redis不存在该k
多个并发请求同时访问
pubilic T redisLock(){
//1、获取分布式锁
Boolean lock=stringRedisTemplate.opsForValue().setIfAbsent("lock","1111");
if(lock){
//2、执行业务逻辑
......
//3、释放锁
stringRedisTemplate.delete("lock");
//4、返回结果
return T;
}else{
//5、获取锁失败
//等待一段时间重新尝试获取锁
return redisLock;
}
}
问题:死锁。在执行业务逻辑部分时由于业务出现异常或者超时、以及由于外部因素影响等等导致没有成功释放锁。造成死锁,让其他请求无法获取到锁。
解决方法:设置一个自动过期时间,防止死锁 。看下面优化版1.0
优化版1.0
stringRedisTemplate.expire("lock",30,TimeUnit.SECONDS);
问题:死锁,在正准备设置过期时间时发生宕机导致死锁。
解决方法:将设置过期时间和获取锁定位一个原子操作,下面看优化版2.0
优化版2.0
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "1111", 30, TimeUnit.SECONDS);
问题:在删除锁时删掉其他线程占用的锁,由于业务A负责可能在业务进行过程中分布式锁已经过期,导致其他线程B可以获取到锁(能够存进Redis),当A完成业务时进行释放锁时将B持有的锁释放了。从而导致加锁失败。
解决方法:在获取锁时给每一个线程设立一个UUID,在释放锁时进行验证。保证每一个线程只删除自己持有的锁。
优化版3.0
//注入
StringRedisTemplate stringRedisTemplate
//原理就是利用Redis set k v NX 指令(setIfAbsent方法), NX表示添加k v如果Redis不存在该k
多个并发请求同时访问
pubilic T redisLock(){
//1、获取分布式锁
String uuid = UUID.randomUUID().toString();
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 30,TimeUnit.SECONDS);
if(lock){
//2、执行业务逻辑
......
//3、释放锁, 释放前先进行验证
String lock = stringRedisTemplate.opsForValue().get("lock");
if (uuid.equals(lock)){
stringRedisTemplate.delete("lock");
}
//4、返回结果
return T;
}else{
//5、获取锁失败
//等待一段时间重新尝试获取锁
return redisLock();
}
}
问题:和获取锁一样,可能在查询时获取的uuid为A线程,但是在返回过程中A持有的锁过期被B所占有。然后进行释放锁时仍然释放的是B线程的锁。
解决方法:将查询锁和删除设置为一个原子操作,由Redis官方文档中可知,可以进行脚本操作。
优化版4.0 最终版
//3、释放锁, 释放前先进行验证
String script="if redis.call(‘get‘,KEYS[1]) == ARGV[1] then return redis.call(‘del‘,KEYS[1]) else return end";
Integer delLock = stringRedisTemplate.execute(new DefaultRedisScript<Integer>(script,Integer.class), Arrays.asList("lock"), uuid);
标签:delete red The second 设立 最简 实现 ring 异常
原文地址:https://www.cnblogs.com/ydmysm/p/study_in_redisLock.html