redis分布式锁的实现和原理

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);

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

2、加强版-添加finally
String lockKey = "key";
try{
    //拿锁
    boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");
    if(!result){
        return "error";
    }
    /*
     * 处理代码
     */
}finally{
    //释放锁
    stringRedisTemplate.delete(lockKey);
}

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

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拿到锁继续执行,线程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);
    }
}

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

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();

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

compile 'org.redisson:redisson:3.12.3'  
@Autowired
private Redisson redisson;
String lockKey = "key";
//获取锁对象
RLock lock = redisson.getLock(lockKey);
try{
    //加锁
   lock.lock(3, TimeUnit.SECONDS);
    /*
     * 处理代码
     */
}finally{
    //释放锁
    lock.unlock();
}

原理图
锁原理图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值