【Redis】实现分布式锁:引导实现过程中出现的问题 以及 给出解决办法

一、分布式锁应用场景介绍

随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁主流的实现方案:

  1. 基于数据库实现分布式锁
  2. 基于缓存(Redis等)
  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高
  2. 可靠性:zookeeper最高

这里,我们就基于redis实现分布式锁。

二、Redis分布式锁第一版

拿到锁后的操作可能存在延迟.
锁过期,后续释放的是别人的锁

//第一版:拿到锁后的操作可能存在延迟,锁过期。后续释放的是别人的锁
    @RequestMapping(value = "/doLock1")
    public String doLock1(){
        String result;
        // 1.获取锁
        Boolean islocked = redisTemplate.opsForValue().setIfAbsent("lock", "lock",50, TimeUnit.MILLISECONDS);
        // 2.判断是否成功获得锁
        // 3.成功:则进行操作
        if(islocked){
            //    3.1 进行操作
            Object num = redisTemplate.opsForValue().get("num");
            if (StringUtils.isEmpty(num)){
                return "操作失败:key不存在!";
            }else{
                result = String.valueOf(redisTemplate.opsForValue().increment("num"));
            }
            //***************************可能存在延迟,key过期。释放的是别人的锁****************************************
            //    3.2 释放锁
            redisTemplate.delete("lock");
            return result;
        }else{
            try {
                TimeUnit.SECONDS.sleep(1);
                // 4.失败:则继续尝试获取
                return doLock1();
            } catch (InterruptedException e) {
                e.printStackTrace();
                return String.valueOf(e);
            }
        }
    }

三、Redis分布式锁第二版

生成uuid,用于辨别锁的拥有者。
但是判断与删除不是原子操作,仍然存在释放别人锁的情况

//第二版:UUID,判断与删除不是原子操作,仍然存在释放别人锁的情况
    @RequestMapping(value = "/doLock2")
    public String doLock2(){
        String result;
        //生成uuid,用于辨别锁的拥有者
        String uuid= String.valueOf(UUID.randomUUID());
        // 1.获取锁
        Boolean islocked = redisTemplate.opsForValue().setIfAbsent("lock", uuid,50, TimeUnit.MILLISECONDS);
        // 2.判断是否成功获得锁
        // 3.成功:则进行操作
        if(islocked){
            //    3.1 进行操作
            Object num = redisTemplate.opsForValue().get("num");
            if (StringUtils.isEmpty(num)){
                return "操作失败:key不存在!";
            }else{
                result = String.valueOf(redisTemplate.opsForValue().increment("num"));
            }
            //    3.2 释放锁
            //判断当前锁是否是自己的
            //******************************判断与删除不是原子操作,仍然存在释放别人锁的情况******************************
            if(uuid.equals(redisTemplate.opsForValue().get("lock"))){
                redisTemplate.delete("lock");
            }
            return result;
        }else{
            try {
                TimeUnit.SECONDS.sleep(1);
                // 4.失败:则继续尝试获取
                return doLock1();
            } catch (InterruptedException e) {
                e.printStackTrace();
                return String.valueOf(e);
            }
        }
    }

四、Redis分布式锁第三版

让LUA脚本包含判断与删除的逻辑,使判断与删除成为原子操作

//第三版:LUA脚本,包含判断与删除的逻辑,使其成为原子操作
    @RequestMapping(value = "/doLock3")
    public String doLock3(){
        String result;
        //生成uuid,用于辨别锁的拥有者
        String uuid= String.valueOf(UUID.randomUUID());
        //定义脚本对象
        DefaultRedisScript scriptObject=new DefaultRedisScript();
        scriptObject.setResultType(Long.class);
        String script="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        scriptObject.setScriptText(script);
        // 1.获取锁
        Boolean islocked = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
        // 2.判断是否成功获得锁
        // 3.成功:则进行操作
        if(islocked){
            //    3.1 进行操作
            Object num = redisTemplate.opsForValue().get("num");
            if (StringUtils.isEmpty(num)){
                return "操作失败:key不存在!";
            }else{
                result = String.valueOf(redisTemplate.opsForValue().increment("num"));
            }
            //    3.2 释放锁:使用LUA脚本 判断并删除锁
            //返回1:删除成功  返回0:已过期
            //KEYS[1] :Arrays.asList("lock")   ARGV[1]:uuid
            redisTemplate.execute(scriptObject, Arrays.asList("lock"), uuid);
            return result;
        }else{
            try {
                TimeUnit.SECONDS.sleep(1);
                // 4.失败:则继续尝试获取
                return doLock1();
            } catch (InterruptedException e) {
                e.printStackTrace();
                return String.valueOf(e);
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿你满腹经纶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值