大纲
1、代码中常见的redis分布式锁
2、redission lock实现,有什么问题,注意事项
3、红锁(redLock),联锁(multiLock)用来解决什么问题,比较
4、看门狗是什么
一、代码中常见的redis分布式锁
代码中常见的写法
boolean redisKeyBoolean = redisTemplate.opsForValue().setIfAbsent("redisKey","666");
如果key不存在设置成功返回true,如果key存在设置失败返回false
每个redisKey单线程访问,多线程访问时只有一个线程成功,其它线程立马返回失败,用户体验不友好,redis主从\集群模式下宕机问题(后面讲解),上面代码不设超时间(如果代码执行中服务宕机rediskey不过期问题),设置超时时间(超时时间内程序没跑完,key超时代码重复执行问题),这里吧问题先抛出来,后面总结
其它情况:这里只是举了这一个例子,还有很多,比如网上搜了一段代码,while循环判断获取锁时间,等等,或者自己造轮子等,不是说不好,只是或多或少存在写问题
二、redission lock实现,有什么问题,注意事项
既然有人把轮子造好了,直接拿过来岂不更好,redission也是我们现在reids最常用的分布式锁轮子,比如
RLock lock = redisson.getLock("myLock")
// 1. 最常见的使用方法
//lock.lock();
// 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
//lock.lock(10, TimeUnit.SECONDS);
//3、
boolean locked = lock.tryLock(waitTime, leaseTime, unit);
//执行业务
lock.unlock();
就是普通的加锁,执行业务,释放锁操作,这应该是我们代码中最常见的解决方案
底层是用lua脚本实现的,确保了redis的原子操作
这种方法大部分情况下是能满足业务的,但也有极端情况,如果redis是集群模式,当主节点加锁成功后尚未同步数据到从节点,主节点挂掉了,这时候主从切换,从节点是没有锁数据的,导致锁丢失
三、红锁(redLock),联锁(multiLock)用来解决什么问题,比较
连锁
/**
* 连锁-只有所有的RedissonClient都锁成功才算成功
*
* @param waitTime 获取锁的等待时间
* @param leaseTime 锁的持续时间
* @param unit 时间的单位
*
* @return 获取锁的结果
*/
public Boolean tryMultiLock(RedissonClient redisson1,RedissonClient redisson2,RedissonClient redisson3, long waitTime, long leaseTime, TimeUnit unit){
RLock lock1 = redisson1.getLock("test:lock1");
RLock lock2 = redisson2.getLock("test:lock2");
RLock lock3 = redisson3.getLock("test:lock23");
RLock lock = redissonClient.getMultiLock(lock1, lock2, lock3);
try {
// 同时加锁:lock1 lock2,lock3 所有的锁都上锁成功才算成功。
lock.lock();
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(waitTime, unit);
return res;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return false;
上面代码,假设redis有三个节点,不分主从,只有这三个节点都拿到锁后才算拿到锁
解决了第二种宕机的情况,当然也带来了新问题,就是全部拿到锁,时间会拉长,效率问题
红锁
/**
* 红锁-只有过半的RedissonClient都锁成功才算成功
*
* @param waitTime 获取锁的等待时间
* @param leaseTime 锁的持续时间
* @param unit 时间的单位
*
* @return 获取锁的结果
*/
public Boolean tryRedLock(RedissonClient redisson1,RedissonClient redisson2,RedissonClient redisson3, long waitTime, long leaseTime, TimeUnit unit){
RLock lock1 = redisson1.getLock("test:lock1");
RLock lock2 = redisson2.getLock("test:lock2");
RLock lock3 = redisson3.getLock("test:lock23");
RLock lock = redissonClient.getRedLock(lock1, lock2, lock3);
try {
// 同时加锁:lock1 lock2,lock3 所有的锁都上锁成功才算成功。
lock.lock();
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(waitTime, unit);
return res;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return false;
跟连锁的区别:连锁需要全部节点都拿到锁才算获取锁成功,红锁是过半拿到锁成功就算成功,丢掉了一部分精度,提高了一些效率
四、看门狗是什么
到目前为止上面提到的问题只有一个还没解决:设置了持有锁的时间,如果程序执行超过持有锁的时间,第二个线程进来导致业务重复执行的问题
看门狗就是干这个的
加锁成功后会启动一个线程,定时(锁持有时间/3),系统默认的锁持有时间30s,也就是默认每10秒去续约,将锁时间重置为30s,直至调用unlock或者服务挂掉超时释放锁
但,看门狗生效是有条件的,上面我们调用lock.trylock()或lock()时不能设置leaseTime或者leaseTime=-1时看门狗才会生效,如果我们设置了leaseTime(持有锁的时间)那便不会生效