广告
返回顶部
首页 > 资讯 > 精选 >SpringBoot如何使用 Redis 分布式锁解决并发问题
  • 669
分享到

SpringBoot如何使用 Redis 分布式锁解决并发问题

2023-06-25 14:06:24 669人浏览 独家记忆
摘要

这期内容当中小编将会给大家带来有关SpringBoot如何使用 Redis 分布式锁解决并发问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。问题背景现在的应用程序架构中,很多服务都是多副本运行,从而保证

这期内容当中小编将会给大家带来有关SpringBoot如何使用 Redis 分布式解决并发问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

    问题背景

    现在的应用程序架构中,很多服务都是多副本运行,从而保证服务的稳定性。一个服务实例挂了,其他服务依旧可以接收请求。但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的:接收到请求后,先查询 DB 看是否有相关的数据,如果没有则插入数据,如果有则更新数据。在这种场景下如果相同的 N 个请求并发发到后端服务实例,就会出现重复插入数据的情况:

    SpringBoot如何使用 Redis 分布式锁解决并发问题

    解决方案

    针对上面问题,一般的解决方案是使用分布式锁来解决。同一个进程内的话用本进程内的锁即可解决,但是服务多实例部署的话是分布式的,各自进程独立,这种情况下可以设置一个全局获取锁的地方,各个进程都可以通过某种方式获取这个全局锁,获得到锁后就可以执行相关业务逻辑代码,没有拿到锁则跳过不执行,这个全局锁就是我们所说的分布式锁。分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。

    我们这里介绍如何基于 Redis 的分布式锁来解决分布式并发问题:Redis 充当获取全局锁的地方,每个实例在接收到请求的时候首先从 Redis 获取锁,获取到锁后执行业务逻辑代码,没争抢到锁则放弃执行。

    SpringBoot如何使用 Redis 分布式锁解决并发问题

    主要实现原理:

    Redis 锁主要利用 Redis 的 setnx 命令:

    加锁命令:SETNX key value,当键不存在时,对键进行设置操作并返回成功,否则返回失败。KEY 是锁的唯一标识,一般按业务来决定命名。Value 一般用 UUID 标识,确保锁不被误解。

    解锁命令:DEL key,通过删除键值对释放锁,以便其他线程可以通过 SETNX 命令来获取锁。

    锁超时:EXPIRE key timeout, 设置 key 的超时时间,以保证即使锁没有被显式释放,锁也可以在一定时间后自动释放,避免资源被永远锁住。

    可靠性:

    为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

    • 互斥性。在任意时刻,保证只有一台机器的一个线程可以持有锁;

    • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁;

    • 具备非阻塞性。一旦获取不到锁就立刻返回加锁失败;

    • 加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了;

    springBoot 集成使用 Redis 分布式锁

    写了一个 RedisLock 工具类,用于业务逻辑执行前加锁和业务逻辑执行完解锁操作。这里的加锁操作可能实现的不是很完善,有加锁和锁过期两个操作原子性问题,如果 SpringBoot 版本是2.x的话是可以用注释中的代码在加锁的时候同时设置锁过期时间,如果 SpringBoot 版本是2.x以下的话建议使用 lua 脚本来确保操作的原子性,这里为了简单就先这样写:

    import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.List;import java.util.concurrent.TimeUnit;@Componentpublic class RedisLock {    @Autowired    StringRedisTemplate redisTemplate;        public boolean lock(String lockKey, String identity, long expireTime) {        // 由于我们目前 springboot 版本比较低,1.5.9,因此还不支持下面这种写法        // return redisTemplate.opsForValue().setIfAbsent(lockKey, identity, expireTime, TimeUnit.SECONDS);        if (redisTemplate.opsForValue().setIfAbsent(lockKey, identity)) {            redisTemplate.expire(lockKey, expireTime, TimeUnit.SECONDS);            return true;        }        return false;    }        public boolean releaseLock(String lockKey, String identity) {        String luaScript = "if " +                "  redis.call('get', KEYS[1]) == ARGV[1] " +                "then " +                "  return redis.call('del', KEYS[1]) " +                "else " +                "  return 0 " +                "end";        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();        redisScript.setResultType(Boolean.class);        redisScript.setScriptText(luaScript);        List<String> keys = new ArrayList<>();        keys.add(lockKey);        Object result = redisTemplate.execute(redisScript, keys, identity);        return (boolean) result;    }}

    使用示例

    这里只贴出关键的使用代码,注意:锁的 key 根据自己的业务逻辑命名,能唯一标示同一个请求即可。value 这里设置为 UUID,为了确保释放锁的时候能正确释放(只释放自己加的锁)。

    @Autowiredprivate RedisLock redisLock;  // redis 分布式锁
    String redisLockKey = String.fORMat("%s:Docker-image:%s", REDIS_LOCK_PREFIX, imageVo.getImageRepository());        String redisLockValue = UUID.randomUUID().toString();        try {            if (!redisLock.lock(redisLockKey, redisLockValue, REDIS_LOCK_TIMEOUT)) {                logger.info("redisLockKey [" + redisLockKey + "] 已存在,不执行镜像插入和更新");                result.setMessage("新建镜像频繁,稍后重试,锁占用");                return result;            }            ... // 执行业务逻辑       catch (Execpion e) {            ... // 异常处理       } finally {  // 释放锁            if (!redisLock.releaseLock(redisLockKey, redisLockValue)) {                logger.error("释放redis锁 [" + redisLockKey + "] 失败);            } else {                logger.error("释放redis锁 [" + redisLockKey + "] 成功");            }        }

    上述就是小编为大家分享的SpringBoot如何使用 Redis 分布式锁解决并发问题了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网精选频道。

    --结束END--

    本文标题: SpringBoot如何使用 Redis 分布式锁解决并发问题

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

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

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

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

    下载Word文档
    猜你喜欢
    • SpringBoot如何使用 Redis 分布式锁解决并发问题
      这期内容当中小编将会给大家带来有关SpringBoot如何使用 Redis 分布式锁解决并发问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。问题背景现在的应用程序架构中,很多服务都是多副本运行,从而保证...
      99+
      2023-06-25
    • 关于SpringBoot 使用 Redis 分布式锁解决并发问题
      目录问题背景解决方案主要实现原理: 可靠性: SpringBoot 集成使用 Redis 分布式锁使用示例参考文档问题背景 现在的应用程序架构中,很多服务都是多副本运行,从而保证服务...
      99+
      2022-11-12
    • 使用redis分布式锁解决并发线程资源共享问题
      前言 众所周知, 在多线程中,因为共享全局变量,会导致资源修改结果不一致,所以需要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操作 但是在分布式架构中,我们的服务可能会有n个实例,但线程锁只对同一...
      99+
      2022-10-18
    • 如何解决redis分布式锁的问题
      这篇文章主要为大家展示了“如何解决redis分布式锁的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决redis分布式锁的问题”这篇文章吧。分布式锁在...
      99+
      2022-10-18
    • 解决python-redis-lock分布式锁的问题
      python-redis-lock 官方文档 不错的博文可参考 问题背景 在使用celery执行我们的异步任务时,为了提高效率,celery可以开启多个进程来启动对应的worke...
      99+
      2022-11-12
    • redis分布式锁释放问题怎么解决
      在Redis中实现分布式锁时,可以使用以下两种方式解决锁释放的问题: 设置锁的过期时间:在获取锁时,设置一个过期时间,超过该时间...
      99+
      2023-10-27
      redis
    • Redis分布式锁解决秒杀超卖问题
      目录分布式锁应用场景单体锁的分类分布式锁核心逻辑分布式锁实现的问题——死锁和解决Redis解决删除别人锁的问题分布式锁应用场景 秒杀环境下:订单服务从库存中心拿到库存数,如果库存总数大于0,则进...
      99+
      2022-07-13
      Redis秒杀超卖 Redis分布式锁
    • Redisson如何解决Redis分布式锁提前释放问题
      目录前言:一、问题描述:二、原因分析:三、解决方案:1、思考: 2、Redisson简单配置:3、使用样例:四、源码分析1、lock加锁操作2、unlock解锁操作总结:相...
      99+
      2022-11-13
    • 怎么解决python-redis-lock分布式锁的问题
      本篇内容介绍了“怎么解决python-redis-lock分布式锁的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!问题背景在使用cele...
      99+
      2023-06-25
    • SpringBoot之如何使用Redis实现分布式锁
      小编给大家分享一下SpringBoot之如何使用Redis实现分布式锁,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!springboot是什么springboot一种全新的编程规范,其设计目的是用来简化新Spring应用的...
      99+
      2023-06-14
    • 如何使用Redis锁处理并发问题详解
      前言 上周“被”上线了一个紧急项目,周五下班接到需求,周一开始思考解决方案,周三开发完成,周四走流程上线,也算是面向领导编程了。之前的项目里面由于是自运维,然后大多数又都赶时间,所以在处理定时任务上面基本都...
      99+
      2022-10-18
    • redis分布式锁如何解决表单重复提交的问题
      本篇文章为大家展示了redis分布式锁如何解决表单重复提交的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次...
      99+
      2023-06-21
    • 详解RedisTemplate下Redis分布式锁引发的系列问题
            自己的项目因为会一直抓取某些信息,但是本地会和线上经常一起跑,造成冲突。这其实就是我们常说的分布式集群的问题了,本地和线...
      99+
      2022-11-11
    • redis分布式锁解决表单重复提交的问题
      假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许因为网速的原因,没有跳转到...
      99+
      2022-11-12
    • 如何解决redis分布式锁超时
      解决redis分布式锁超时的方法:当锁超时时间快到期且逻辑未执行完,可延长锁超时时间。示例:if redis.call("get",KEYS[1]) == ARGV[1] thenredis.call("set",KEYS[1],ex=30...
      99+
      2022-10-11
    • 基于Mongodb分布式锁解决定时任务并发执行问题
      目录前言All in one Code原理前言 我们日常开发过程,会有一些定时任务的代码来统计一些系统运行数据,但是我们应用有需要部署多个实例,传统的通过配置文件来控制定时任务是否启动又太过繁琐,而且还经常出错,导致一些...
      99+
      2023-04-20
      Mongodb分布式锁 Mongodb定时任务并发执行
    • 如何解决redis的并发问题
      如何解决redis的并发问题?针对这个问题,今天小编总结了这篇文章,希望能帮助更多想解决这个问题的朋友找到更加简单易行的办法。redis中的并发问题使用redis作为缓存已经很久了,redis是以单进程的形...
      99+
      2022-10-18
    • Redis如何解决高并发问题
      这篇文章主要介绍Redis如何解决高并发问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体如下:redis为什么会有高并发问题redis的出身决定 redis是一种单线...
      99+
      2022-10-18
    • 如何在 PHP 分布式系统中使用缓存解决高并发问题?
      在现代互联网应用程序中,高并发是一个非常普遍的问题。PHP 分布式系统中,缓存是一个非常重要的组件,可以帮助我们解决高并发问题。在本文中,我们将讨论如何在 PHP 分布式系统中使用缓存来解决高并发问题。 一、什么是缓存? 缓存是一种用于存...
      99+
      2023-09-22
      分布式 并发 缓存
    • Redis的setNX分布式锁超时时间失效 -1问题及解决
      目录Redis setNX分布式锁超时时间失效 -1使用SETNX加锁使用 RedisTemplate 操作Redis解决死锁Redis分布式锁,超时问题的处理分布式可能会出现的超时问题总结Redis setNX分布式锁...
      99+
      2023-01-09
      Redis分布式锁 setNX分布式锁超时时间失效 RedissetNX分布式锁超时
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作