码迷,mamicode.com
首页 > 其他好文 > 详细

Redis实现简单分布式锁原理

时间:2020-11-26 15:06:25      阅读:8      评论:0      收藏:0      [点我收藏+]

标签: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);

Redis实现简单分布式锁原理

标签:delete   red   The   second   设立   最简   实现   ring   异常   

原文地址:https://www.cnblogs.com/ydmysm/p/study_in_redisLock.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!