Spring Boot 注解加持,轻松实现Redis分布式锁,高效并发由你掌控

文章导读
通过在Spring Boot中使用自定义注解结合Redis,你可以轻松实现分布式锁,从而高效控制并发。
📋 目录
  1. A Spring Boot 注解加持,轻松实现Redis分布式锁,高效并发由你掌控
  2. B 为什么需要分布式锁?
  3. C 动手制作你的锁:核心思路
  4. D 让锁用起来像注解一样简单
  5. E 三步实现注解式分布式锁
  6. F 需要注意的几个小坑
  7. G FAQ
A A

Spring Boot 注解加持,轻松实现Redis分布式锁,高效并发由你掌控

通过在Spring Boot中使用自定义注解结合Redis,你可以轻松实现分布式锁,从而高效控制并发。

为什么需要分布式锁?

想象一下,一个商城系统里,“秒杀”活动刚开始,成千上万的用户同时点击“购买”按钮,如果没有任何控制,后台系统可能会对同一件商品反复扣减库存,最后卖出的数量远超库存量,这会造成严重的错误。在传统的单机应用中,我们可以用Java自带的锁(比如synchronized关键字)来保证同一时间只有一个线程能执行扣库存的代码。但现在的应用大多是部署在多台服务器上的,这些锁只对同一台服务器内的线程有效。A服务器上的锁管不了B服务器上的线程,这就乱套了。所以,我们需要一把所有服务器都能看见和遵守的“全局锁”,这就是分布式锁。Redis因为速度快、支持原子操作,是制作这把“全局锁”的理想工具。

动手制作你的锁:核心思路

用Redis造锁,核心就是利用它的SETNX命令(现在更推荐用SET命令带NX参数)。这个命令的意思是:只有当这个Key不存在时,才能设置成功。我们可以把一个唯一的标识(比如“商品ID_秒杀活动ID”)作为Key,设置成功就代表“抢到了锁”。为了防止抢到锁的服务器万一崩溃,导致锁永远不释放,我们还要给这个Key设置一个过期时间。最后,业务代码执行完毕,必须主动删除这个Key来“释放锁”。整个流程就是“争抢锁 -> 执行核心业务 -> 释放锁”。

让锁用起来像注解一样简单

如果每次都要写“抢锁、放锁”的代码,会很麻烦也容易出错。Spring Boot的AOP(面向切面编程)特性可以帮我们大忙。我们可以定义一个自己的注解,比如叫@RedisLock。然后,你只需要在想加锁的方法上,贴上这个@RedisLock注解就行了,剩下的“抢锁”和“放锁”的脏活累活,全部交给背后的代码自动完成。

三步实现注解式分布式锁

第一步,定义注解。创建一个名为@RedisLock的接口。你可以在注解里放一些参数,比如锁的名字(lockKey),锁的过期时间(expireTime,单位秒),这样用起来更灵活。

第二步,编写切面逻辑。这是最核心的一步。你需要创建一个“切面”类,使用@Aspect和@Component注解标记它。在这个类里,定义一个方法,专门“环绕”处理所有加了@RedisLock注解的方法。这个方法要做的事情是:1. 在目标方法执行前,去Redis里尝试“加锁”。加锁的Key可以用注解参数和方法参数组合,确保唯一性。这里必须用Redis的原子操作(如setIfAbsent)并设置过期时间。如果加锁失败(说明别人已经持有了锁),就直接抛出异常或返回,阻止方法继续执行。2. 如果加锁成功,就执行你原本的业务方法。3. 无论业务方法是成功完成还是抛出异常,最后都必须在finally块里,“删除”Redis中的那个Key来释放锁。这里要小心,只能删除自己加的锁,可以用一个随机值作为锁的Value,删除前先对比一下,避免误删了别人的锁。

Spring Boot 注解加持,轻松实现Redis分布式锁,高效并发由你掌控

第三步,投入使用。在你需要控制并发的方法上,比如扣减库存的service方法,加上一句@RedisLock(lockKey = \"seckill:\" + #{productId}, expireTime = 10)。现在,当这个方法被调用时,切面会自动拦截,先去Redis里争夺名为“seckill:商品ID”的锁,抢到了才执行扣库存,执行完自动释放。整个过程对你来说,就像写了个普通方法一样简单。

需要注意的几个小坑

锁的过期时间要设得合理:太短,业务没执行完锁就没了,会导致混乱;太长,万一持有锁的服务宕机,其他服务要等很久才能继续。通常要根据业务执行时间评估,并留一些余量。释放锁时一定要检查是不是自己的锁,前面提到的“随机Value比对删除”是常见做法。对于特别复杂的场景,你可能还需要考虑“锁续期”的问题,也就是在业务执行时间可能超过锁过期时间时,启动一个后台线程定期去延长锁的过期时间。

FAQ

问:除了Redis,还能用什么实现分布式锁?

答:当然可以。ZooKeeper和etcd也是常用的选择。它们利用临时顺序节点的特性来实现锁,可能在某些场景下更可靠(比如能有效解决锁过期释放的问题),但通常性能不如Redis。Redis方案在性能和高可用之间取得了不错的平衡,是互联网应用中非常主流的选择。

Spring Boot 注解加持,轻松实现Redis分布式锁,高效并发由你掌控

问:设置了过期时间,为什么还要主动释放锁?

答:这是一种双重保障和资源优化的良好习惯。如果业务正常执行完毕,我们立刻释放锁,其他等待的线程就能立即获得锁并开始工作,这大大提高了系统的效率和响应速度。如果只依赖过期时间,其他线程就必须傻等到锁自动过期,这段时间就被浪费了。主动释放是对系统资源更友好的做法。

问:在Spring Boot项目中具体如何引入Redis?

答:非常简单。在你的pom.xml文件中,加入Spring Boot Data Redis的依赖,比如‘org.springframework.boot:spring-boot-starter-data-redis’。然后在application.yml或application.properties配置文件里,写好你的Redis服务器地址、端口(如果有密码也要配上)。Spring Boot会自动帮你配置好RedisTemplate等工具,你就可以直接在代码里注入使用了。

引用来源:本文实现思路基于Redis官方对SET命令NX、PX参数的说明,以及Spring Framework官方文档中关于AOP和自定义注解的指导。具体代码实践参考了Spring Boot社区中广泛采用的分布式锁设计模式。