当前位置:   article > 正文

redis分布式锁的实现和原理_opsforvalue.setifabsent使用的哪个加锁的命令

opsforvalue.setifabsent使用的哪个加锁的命令
1、简单版本

命令:setnx key value
对应RedisTemplate方法:setIfAbsent

在指定的 key 不存在时,为 key 设置指定的值

String lockKey = "key";
//拿锁
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");
if(!result){
    return "error";
}
/*
 * 处理代码
 */
//释放锁
stringRedisTemplate.delete(lockKey);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

容易出现的问题:出现异常容易死锁

2、加强版-添加finally
String lockKey = "key";
try{
    //拿锁
    boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");
    if(!result){
        return "error";
    }
    /*
     * 处理代码
     */
}finally{
    //释放锁
    stringRedisTemplate.delete(lockKey);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

容易出现的问题:程序终止发生死锁

3、加强版-设置超时时间
String lockKey = "key";
try{
    //拿锁
    boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"azheng",10,TimeUnit.SECONDS);
    if(!result){
        return "error";
    }
    /*
     * 处理代码
     */
}finally{
    //释放锁
    stringRedisTemplate.delete(lockKey);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

容易出现的问题:(执行超时,锁提前被释放问题)高并发下容易出现问题,线程1在程序没有执行完情况下锁过期被释放,但是,这时候线程2拿到锁继续执行,线程2还未执行完的时候,锁这时候却被线程1释放。这样会一直无线循环下去锁出现永久失效。。。锁一直被其他线程提前释放。

4、升级版-添加线程标识UUID
String clientId = UUID.randomUUID().toString();
String lockKey = "key";
try{
    //拿锁
    boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10,TimeUnit.SECONDS);
    if(!result){
        return "error";
    }
    /*
     * 处理代码
     */
}finally{
    if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
        //释放锁
        stringRedisTemplate.delete(lockKey);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

容易出现的问题:锁不会被其他线程释放,但锁还是会被超时释放,会被其它线程拿到锁。

5、高级版-添加守护线程

解决锁超时带来的一系列问题

守护线程定时扫描的时间为: 锁超时时间的三分之一左右。

不存在性能问题,因为只有拿到锁的人才会有守护线程。但实际上同一时刻只有一个线程会拿到锁。

容易出现的问题:redis 宕机之后锁数据丢失,重启后导致两个线程同时持有锁,比如线程1拿到锁执行逻辑,然后此时Redis重启导致锁的数据丢了,然后线程2又可以拿到锁了。

6、终极版-使用 redis 红锁

Redis作者引入红锁的概念,就是集群模式下获取锁的时候,必须在一半以上的节点都获取到锁才算成功,保障锁数据不丢失。

基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

以上逻辑自行实现过于复杂,所以需要引入第三方包Redisson.

compile 'org.redisson:redisson:3.12.3'  
  • 1
@Autowired
private Redisson redisson;
  • 1
  • 2
String lockKey = "key";
//获取锁对象
RLock lock = redisson.getLock(lockKey);
try{
    //加锁
   lock.lock(3, TimeUnit.SECONDS);
    /*
     * 处理代码
     */
}finally{
    //释放锁
    lock.unlock();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

原理图
锁原理图

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/696780
推荐阅读
相关标签
  

闽ICP备14008679号