微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

原文地址:https://www.jb51.cc/wenti/3281958.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐