广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >详解redis分布式锁(优化redis分布式锁的过程及Redisson使用)
  • 262
分享到

详解redis分布式锁(优化redis分布式锁的过程及Redisson使用)

2024-04-02 19:04:59 262人浏览 独家记忆
摘要

目录1. Redis在实际的应用中2.如何使用redis的功能进行实现分布式锁2.1 redis分布式锁思想2.1.1设计思想:2.1.2 根据上面的设计思想进行代码实现2.2 使用

1. redis在实际的应用中

不仅可以用来缓存数据,在分布式应用开发中,经常被用来当作分布式锁的使用,为什么要用到分布式锁呢?

在分布式的开发中,以电商库存的更新功能进行讲解,在实际的应用中相同功能的消费者是有多个的,假如多个消费者同一时刻要去消费一条数据,假如业务逻辑处理逻辑是查询出redis中的商品库存,而如果第一个进来的消费的消费者获取到库存了,还没进行减库存操作,相对晚来的消费者就获取了商品的库存,这样就导致数据会出错,导致消费的数据变多了。

例如:消费者A和消费者B分别去消费生产者C1和生产者C2的数据,而生产者都是使用同一个redis的数据库的,如果生产者C1接收到消费者A的消息后,先进行查询库存,然后当要进行减库存的时候,因为生产者C2接收到消费者B的消息后,也去查询库存,而因为生产者C1还没有进行库存的更新,导致生产者C2获取到的库存数是脏数据,而不是生产者C1更新后的数据,导致业务出错。

在这里插入图片描述

如果不是分布式的应用,可以使用synchronized进行防止库存更新的问题的产生,但是synchronized只是基于JVM层面的,如果在不同的JVM中,就不能实现这样的功能。


   @GetMapping("getInt0")
    public String test() {
        synchronized (this) {
            //获取当前商品的数量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后对商品进行出库操作,即进行减1
            
            if (productNum > 0) {
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                int productNumNow = productNum - 1;
            } else {
                return "product=0";
            }
            int productNumNow = productNum - 1;
            return "success=" + productNumNow;
        }
    }

2.如何使用redis的功能进行实现分布式锁

2.1 redis分布式锁思想

如果对redis熟悉的话,我们能够想到redis中具有setnx的命令,该命令的功能宇set功能类似,但是setnx的命令在进行存数据前,会检查redis中是否已经存在相同的key,如存在的话就返回false,反之则返回true,因此我们可以使用该命令的功能,设计一个分布式锁。

2.1.1设计思想:

  • 在请求相同功能的接口时,使用redis的setnx命令,如果使用setnx命令后返回的是为true,说明此时没有其他的调用这个接口,就相当于获取到锁了,然后就可以继续执行接下来的业务逻辑了。当执行完业务逻辑后,在返回数据前,就把key删除了,然后其他的请求就能获取到锁了。
  • 如果使用setnx命令,返回的是false,说明此时有其他的消费者正在调用这个接口,因此需要等待其他消费者顺利消费完成后,才能获取到分布式的锁。

2.1.2 根据上面的设计思想进行代码实现

代码片段【1】


  @GetMapping("getInt1")
    public String fubushisuo(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一样,如果redis中已经存在相同的key,则返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        String lockvalue = "yigehaimeirumengdechengxuyuan";
        boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey,lockvalue);
        //如果能够成功的设置lockkey,这说明当前获取到分布式锁
        if (!opsForSet){
            return "false";
        }
        //获取当前商品的数量
        int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
        //然后对商品进行出库操作,即进行减1
        
        if (productNum>0){
            stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
            int productNumNow = productNum - 1;
        }else {
            return "product=0";
        }
        //然后进行释放锁
        stringRedisTemplate.delete(lockkey);
        int productNumNow = productNum-1;
        return "success="+productNumNow;
    }


2.1.2.1反思代码片段【1】

如果使用这种方式,会产生死锁的方式:
死锁发生的情况:
(1) 如果在a业务逻辑出现错误时,导致不能执行delete()操作,使得其他的请求不能获取到分布式锁,业务lockkey一直存在于reids中,导致setnx操作一直失败,所以不能获取到分布式锁
(2) 解决方法,使用对业务代码进行try…catch操作,如果出现错误,那么使用finally对key进行删除

优化代码【2】


     @GetMapping("getInt2")
    public String fubushisuo2(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一样,如果redis中已经存在相同的key,则返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        String lockvalue = "yigehaimeirumengdechengxuyuan";
        boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey,lockvalue);
        int productNumNow = 0;
        //如果能够成功的设置lockkey,这说明当前获取到分布式锁
        if (!opsForSet){
            return "false";
        }
        try {
            //获取当前商品的数量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后对商品进行出库操作,即进行减1
            
            if (productNum>0){
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                productNumNow = productNum-1;
            }else {
                return "product=0";
            }

        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
                //然后进行释放锁
            stringRedisTemplate.delete(lockkey);
        }

        return "success="+productNumNow;
    }
2.1.2.2反思代码【2】

出现问题的情况:
如果这种情况也有会产生的情况,如果此时有多台服务器都在运行该方法,
其中有一个方法获取到了分布式锁,而在运行下面的业务代码时,此时该服务器突然宕机了,导致其他的不能获取到分布式锁,

解决方法:加上过期时间,但又服务宕机了,过了设置的时间后,redis会可以把key给删除,这样其他的的服务器就可以正常的进行上锁了。

优化代码【3】


 @GetMapping("getInt3")
    public String fubushisuo3(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一样,如果redis中已经存在相同的key,则返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        String lockvalue = "yigehaimeirumengdechengxuyuan";
       //[01] boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey,lockvalue);
        //设置过期时间为10秒,但是如果使用该命令,没有原子性,可能执行expire前宕机了,而不是设置过期时间,
       //[02] stringRedisTemplate.expire(lockkey, Duration.ofSeconds(10));
        //使用setIfAbsent(lockkey,lockvalue,10,TimeUnit.SECONDS);代码代替上面[01],[02]行代码
        Boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, lockvalue, 10, TimeUnit.SECONDS);
        int productNumNow = 0;
        //如果能够成功的设置lockkey,这说明当前获取到分布式锁
        if (!opsForSet){
            return "false";
        }
        try {
            //获取当前商品的数量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后对商品进行出库操作,即进行减1
            
            if (productNum>0){
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                productNumNow = productNum-1;
            }else {
                return "product=0";
            }

        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
        
        //然后进行释放锁
            stringRedisTemplate.delete(lockkey);
        }
        
        return "success="+productNumNow;
    }

2.1.2.3 反思优化代码【3】

出现问题的情况:
如果c业务逻辑持续超过了设置时间,导致redis中的lockkey过期了,
而其他的用户此时访问该方法时获取到锁了,而在此时,之前的的c业务逻辑也执行完成了,但是他会执行delete,把lcokkey删除了。导致分布式锁出错。
例子:在12:01:55的时刻,有一个A来执行该getInt3方法,并且成功获取到锁,但是A执行了10秒后还不能完成业务逻辑,导致redis中的锁过期了,而在11秒的时候有B来执行getint3方法,因为key被A删除了,导致B能够成功的获取redis锁,而在B获取锁后,A因为执行完成了,然后把reids中的key给删除了,但是我们注意的是,A删除的锁是B加上去的,而A的锁是因为过期了,才被redis自己删除了,因此这导致了C如果此时来时也能获取redis分布式锁

解决方法:使用UUID,产生一个随机数,当要进行delete(删除)redis中key时,判断是不是之前自己设置的UUID

代码优化【4】


  @GetMapping("getInt4")
    public String fubushisuo4(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一样,如果redis中已经存在相同的key,则返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        //获取UUID
        String lockvalue = UUID.randomUUID().toString();
        Boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, lockvalue, 10, TimeUnit.SECONDS);
        int productNumNow = 0;
        //如果能够成功的设置lockkey,这说明当前获取到分布式锁
        if (!opsForSet){
            return "false";
        }
        try {
            //获取当前商品的数量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后对商品进行出库操作,即进行减1
            

            if (productNum>0){
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                productNumNow = productNum-1;
            }else {
                return "product=0";
            }

        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
        
        //进行释放锁
            if (lockvalue==stringRedisTemplate.opsForValue().get(lockkey)){
                stringRedisTemplate.delete(lockkey);
            }
        }
        return "success="+productNumNow;
    }
2.1.2.4 反思优化代码【4】

出现问题的情况:
此时该方法是比较完美的,一般并发不是超级大的情况下都可以进行使用,但是关于key的过期时间需要根据业务执行的时间,进行设置,防止在业务还没执行完时,key就过期了.

解决方法:目前有很多redis的分布式锁的框架,其中redisson用的是比较多的

2.2 使用redisson进行实现分布式锁

先添加redisson的Maven依赖


        <dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson</artifactId>
			<version>3.11.1</version>
		</dependency>

redisson的bean配置


@Configuration
public class RedissonConfigure {
    @Bean
    public Redisson redisson(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://27.196.106.42:6380").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

实现分布式锁代码如下


 @GetMapping("getInt5")
    public String fubushisuo5(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一样,如果redis中已经存在相同的key,则返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        //获取UUID
        RLock lock = redisson.getLock(lockkey);
        lock.lock();
        int productNumNow = 0;
        //如果能够成功的设置lockkey,这说明当前获取到分布式锁

        try {
            //获取当前商品的数量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后对商品进行出库操作,即进行减1
            
            if (productNum>0){
            stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
            productNumNow = productNum-1;
            }else {
                return "product=0";
            }
        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
           lock.unlock();
            }

        //然后进行释放锁
        return "success="+productNumNow;
    }

从面就能看到,redisson实现分布式锁是非常简单的,只要简单的几条命令就能实现分布式锁的功能的。
redisson实现分布式锁的只要原理如下:
redisson使用了lua脚本语言使得命令既有原子性,redisson获取锁时,会给key设置30秒的过期是按,同时redisson会记录当前请求的线程编号,然后定时的去检查该线程的状态,如果还处于执行状态的话,而且key差不多要超期过时时,redisson会修改key的过期时间,一般增加10秒。这样就可以动态的设置key的过期时间了,弥补了优化代码【4】的片段

到此这篇关于redis分布式锁详解(优化redis分布式锁的过程及Redisson使用)的文章就介绍到这了,更多相关redis分布式锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解redis分布式锁(优化redis分布式锁的过程及Redisson使用)

本文链接: https://www.lsjlt.com/news/157160.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解redis分布式锁(优化redis分布式锁的过程及Redisson使用)
    目录1. redis在实际的应用中2.如何使用redis的功能进行实现分布式锁2.1 redis分布式锁思想2.1.1设计思想:2.1.2 根据上面的设计思想进行代码实现2.2 使用...
    99+
    2022-11-12
  • 基于Redis分布式锁Redisson及SpringBoot集成Redisson
    目录- 分布式锁需要具备的条件和刚需- Redisson使用- SpringBoot集成Redisson- 分布式锁需要具备的条件和刚需 独占性:OnlyOne,任何时刻只能有且仅有...
    99+
    2022-11-13
  • Java-Redis-Redisson分布式锁的功能使用及实现
    目录前置基础设施功能使用和介绍其他悲观锁的实现方式前置 Java-Redis-Redisson配置基础上我们进行了改造,让锁的使用更加方便 基础设施 RedissonLock imp...
    99+
    2022-11-13
    Java Redis Redisson分布式锁 Java  Redisson分布式锁
  • Redisson实现Redis分布式锁的几种方式
    目录Redis几种架构 普通分布式锁 单机模式 哨兵模式 集群模式 总结 Redlock分布式锁 实现原理 问题合集 前几天发的一篇文章《Redlock:Redis分布式锁最牛逼的实...
    99+
    2022-11-12
  • Redis实现分布式锁详解
    目录一、前言为什么需要分布式锁?二、基于Redis实现分布式锁为什么redis可以实现分布式锁?如何实现?锁的获取锁的释放三、如何避免死锁?锁的过期时间如何设置?避免死锁锁过期处理释放其他服务的锁如何处理呢?那么redi...
    99+
    2023-04-09
    Redis实现分布式锁 Redis分布式锁
  • redis分布式锁优化的实现
    对于单机的应用来说,可以直接使用synchronized关键字或着Lock工具类来加锁;但是对于分布式应用我们需要凭借一些工具来实现加锁; 加锁流程通俗来解释就是:  &...
    99+
    2022-11-12
  • 分布式锁的原理及Redis怎么实现分布式锁
    这篇文章主要介绍“分布式锁的原理及Redis怎么实现分布式锁”,在日常操作中,相信很多人在分布式锁的原理及Redis怎么实现分布式锁问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解...
    99+
    2023-02-02
    redis
  • 详解redis分布式锁的这些坑
    目录一、白话分布式二、分布式锁何为分布式锁分布式锁的条件分布式锁的实现三、redis实现分布式锁四、redis实现分布式锁问题五、用锁遇到过哪些问题?又是如何解决的?未关闭资源B的锁...
    99+
    2022-11-12
  • 怎么使用redis分布式锁
    本篇内容介绍了“怎么使用redis分布式锁”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. redis在实际的应用中不仅可以用来缓存数据,...
    99+
    2023-06-25
  • redis分布式锁怎么使用
    使用Redis分布式锁的一般步骤如下: 获取锁:在Redis中使用SET命令尝试设置一个带有过期时间的键值对作为锁。可以使用命令...
    99+
    2023-10-21
    redis
  • redis实现分布式锁实例详解
    目录1、业务场景引入2、基础环境准备2.1.准备库存数据库2.2.创建SpringBoot工程,pom.xml中导入依赖,请注意版本。2.3.application.properti...
    99+
    2022-11-13
  • 详解redis如何实现分布式锁
    小编这次要给大家分享的是详解redis如何实现分布式锁,文章内容丰富,感兴趣的小伙伴可以来了解一下,希望大家阅读完这篇文章之后能够有所收获。前言系统的不断扩大,分布式锁是最基本的保障。与单机的多线程不一样的...
    99+
    2022-10-18
  • Redis如何实现分布式锁详解
    目录一、前言二、实现原理2.1 加锁2.2 解锁三、通过RedisTemplate实现分布式锁四、通过Redisson实现一、前言 在Java的并发编程中,我们通过锁,来避免由于竞争...
    99+
    2022-11-12
  • 详解基于redis实现分布式锁
    目录前言原理剖析实现编写注解拦截器拦截上述提及工具问题分析前言 为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchroniz...
    99+
    2022-11-12
  • redis分布式锁的实现原理详解
    首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 1.互斥性。在任意时刻,只有一个客户端能持有锁。 2.不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有...
    99+
    2022-11-13
  • Redisson分布式闭锁RCountDownLatch的使用详细讲解
    目录一、RCountDownLatch的使用二、trySetCount()设置计数器三、countDown()源码四、await()源码本篇文章基于redisson-3.17.6版本...
    99+
    2023-02-11
    Redisson分布式闭锁 Redisson RCountDownLatch Redisson分布式闭锁RCountDownLatch
  • Redisson如何解决Redis分布式锁提前释放问题
    目录前言:一、问题描述:二、原因分析:三、解决方案:1、思考: 2、Redisson简单配置:3、使用样例:四、源码分析1、lock加锁操作2、unlock解锁操作总结:相...
    99+
    2022-11-13
  • Redis分布式锁介绍与使用
    目录分布式锁业务逻辑分析Redis命令代码实现分布式锁误删问题问题原因分析代码实现Lua脚本首先,使用idea模拟搭建一个tomcat服务器集群,并使用Nginx对集群中的服务器实现...
    99+
    2022-11-13
  • 详解使用Redis SETNX 命令实现分布式锁
    使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法。 SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key 不存在。 ...
    99+
    2022-06-04
    分布式 详解 命令
  • 详解Redis 分布式锁遇到的序列化问题
    场景描述 最近使用 Redis 遇到了一个类似分布式锁的场景,跟 Redis 实现分布式锁类比一下,就是释放锁失败,也就是缓存删不掉。又踩了一个 Redis 的坑…… 这是什么个情...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作