文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-06-25 14:39

关注

这期内容当中小编将会给大家带来有关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 分布式锁解决并发问题了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯