当前位置: 首页 > news >正文

Spring Boot 中 Redis 分布式锁的正确使用姿势,我后悔没早知道

Spring Boot 中 Redis 分布式锁的正确使用姿势,我后悔没早知道

上个月项目里一个定时任务重复执行了两次,导致数据被重复处理,查了很久才发现是分布式锁失效了。当时我用的是 Redis SETNX 命令,自认为写得没问题,结果在并发场景下完全不行。这篇文章把踩过的坑和正确的做法分享出来,希望你们别再重蹈覆辙。

一、为什么你的分布式锁总失效?

先说说我踩的第一个坑。当时我自信满满地写了这样的代码:

publicBooleantryLock(StringlockKey,StringrequestId){Booleanresult=redisTemplate.opsForValue().setIfAbsent(lockKey,requestId,10,TimeUnit.SECONDS);returnresult;}

看起来挺完美对吧?设置了 10 秒过期时间,防止锁死。但是问题来了:

问题场景:线程 A 获取锁后开始执行任务,执行到一半时业务逻辑耗时超过了 10 秒,锁自动过期释放。此时线程 B 正好进来,也成功获取了锁。这样 A 和 B 同时在执行同一任务,数据就被重复处理了。

根本原因:我没有正确理解 Redis 分布式锁的"看门狗"机制。锁的过期时间应该由业务执行时间动态决定,而不是写死一个固定值。

二、Redis 分布式锁的核心原理

想用好分布式锁,先得搞懂它的核心原理。Redis 分布式锁本质上是利用 SETNX(SET if Not eXists)命令的原子性:

// 伪代码:获取锁if(redis.setnx(lockKey,requestId)==1){// 获取成功,设置过期时间redis.expire(lockKey,30);try{// 业务逻辑}finally{// 释放锁if(requestId.equals(redis.get(lockKey))){redis.del(lockKey);}}}

但这段代码有三个致命问题

问题1:SETNX 和 EXPIRE 不是原子操作
如果程序在 SETNX 成功后、EXPIRE 执行前崩溃,锁就永远不会过期。

问题2:释放锁时没有验证价值
如果线程 A 的锁过期了,线程 B 获取了新锁,此时线程 A 执行完毕去释放锁,会把线程 B 的锁给释放掉。

问题3:无法自动续期
业务执行时间超过锁过期时间,锁自动失效,其他线程可以进来搞破坏。

三、Redisson 框架的正确打开方式

说了这么多坑,该上正确的解决方案了。Redisson 是 Java 中最成熟的 Redis 客户端之一,它帮我们封装好了分布式锁的所有细节。

1. 引入依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.24.3</version></dependency>

2. 配置 Redisson 客户端

@ConfigurationpublicclassRedissonConfig{@Value("${spring.data.redis.host}")privateStringhost;@Value("${spring.data.redis.port}")privateStringport;@BeanpublicRedissonClientredissonClient(){Configconfig=newConfig();Stringaddress="redis://"+host+":"+port;config.useSingleServer().setAddress(address).setPassword("yourpassword")// 如果有密码.setConnectionPoolSize(64).setConnectionMinimumIdleSize(10);returnRedisson.create(config);}}

3. 使用分布式锁

@ServicepublicclassOrderService{@AutowiredprivateRedissonClientredissonClient;publicvoidcreateOrder(Orderorder){StringlockKey="order:lock:"+order.getProductId();RLocklock=redissonClient.getLock(lockKey);try{// 等待锁超时时间 10 秒,自动解锁时间 30 秒// watchDog 会自动续期lock.lock(30,TimeUnit.SECONDS);// 业务逻辑:检查库存、创建订单、扣减库存checkStock(order);saveOrder(order);deductStock(order);}catch(Exceptione){log.error("Order creation failed",e);thrownewRuntimeException("创建订单失败",e);}finally{// 释放锁if(lock.isHeldByCurrentThread()){lock.unlock();}}}}

这样写有几个好处:

  • 自动续期:Redisson 内置看门狗机制,默认每 10 秒检查一次,如果业务还在执行,自动续期 30 秒
  • 原子操作:获取锁和设置过期时间是原子执行的
  • 可重入:同一个线程可以多次获取同一把锁

四、常见坑点与最佳实践

坑点1:锁的 key 设计不合理

错误做法:所有业务都用同一个 key"lock"

正确做法:按业务维度设计 key,例如:

// 订单业务StringlockKey="lock:order:"+orderId;// 库存业务StringlockKey="lock:stock:"+productId;// 分布式环境下,用 UUID + 线程 ID 确保唯一性StringrequestId=UUID.randomUUID().toString()+":"+Thread.currentThread().getId();

坑点2:释放锁时没有验证 owner

错误做法

// 直接删除,不检查是谁的锁redisTemplate.delete(lockKey);

正确做法

// Redisson 自动处理了这个逻辑// 释放锁时会验证是否是当前线程持有的锁lock.unlock();

如果用原生 Redis,要用 Lua 脚本保证原子性:

Stringscript="if redis.call('get', KEYS[1]) == ARGV[1] then "+"return redis.call('del', KEYS[1]) else return 0 end";redisTemplate.execute(newDefaultRedisScript<>(script,Long.class),Collections.singletonList(lockKey),requestId);

坑点3:没有处理获取锁失败的情况

错误做法:获取不到锁就直接报错

正确做法:根据业务场景选择等待策略

// 策略1:等待并重试booleanacquired=lock.tryLock(10,30,TimeUnit.SECONDS);if(acquired){try{// 业务逻辑}finally{lock.unlock();}}else{// 获取锁失败,执行降级逻辑log.warn("Failed to acquire lock, executing fallback");executeFallback();}// 策略2:立即返回,不阻塞if(lock.tryLock()){try{// 业务逻辑}finally{lock.unlock();}}else{// 直接返回结果或提示用户returnResult.fail("系统繁忙,请稍后重试");}

坑点4:集群环境下锁失效

如果 Redis 是主从架构,主节点宕机时可能导致锁丢失。建议使用 RedLock 算法或 Redis Cluster:

// Redisson RedLock 配置RLocklock1=redissonClient1.getLock(lockKey);RLocklock2=redissonClient2.getLock(lockKey);RLocklock3=redissonClient3.getLock(lockKey);RedissonRedLockredLock=newRedissonRedLock(lock1,lock2,lock3);redLock.lock();try{// 业务逻辑}finally{redLock.unlock();}

写在最后

分布式锁是微服务架构中的必备技能,用错了轻则数据重复,重则资金损失。我在项目里血的教训总结下来就三点:

  1. 用成熟的框架:别自己造轮子,Redisson 已经处理了大部分边界情况
  2. 注意锁的粒度:key 设计要足够细,避免影响并发性能
  3. 做好失败降级:获取不到锁时要有兜底方案,别把所有请求都堵死

希望这篇文章能帮你们少踩坑。如果觉得有用,点个赞再走~


相关推荐

  • Redisson 官方文档
  • Redis 分布式锁详解
http://www.jsqmd.com/news/389291/

相关文章:

  • 2026年质量好的芜湖短视频运营培训/芜湖短视频运营综合推荐公司 - 品牌宣传支持者
  • 2026年知名的真空加热压片机/天津荧光压片机制造厂家 - 品牌宣传支持者
  • 2026年靠谱的低能耗搅拌器/桨式搅拌器厂家推荐及选择指南 - 品牌宣传支持者
  • 2026年靠谱的可程式恒温恒湿试验箱/恒温恒湿试验箱厂家推荐及选择参考 - 品牌宣传支持者
  • 2026年比较好的立式加工中心/高压清洗机加工中心高评分品牌推荐(畅销) - 品牌宣传支持者
  • 2026年知名的圆盘造粒机复合肥设备/BB肥复合肥设备实力厂家 - 品牌宣传支持者
  • 2026年热门的缓释尿素包衣设备/硫包衣尿素包衣设备生产线厂家推荐及采购指南 - 品牌宣传支持者
  • sizebox gts growth lua代码参考(可用于blender python编程控制动画)
  • Claude Code 实战:从 Plan 模式到多 Agent 并行,NewsFlow 升级实录
  • 杰理之外部添加多EQ文件的方法【篇】
  • 大数据毕设最全课题分享
  • 嵌入式框架架构设计与中断处理机制
  • # 高危预警|CVE-2026-24735漏洞突袭Apache Answer,私密/已删帖子历史记录无差别泄露
  • GESP认证C++编程真题解析 | 202503 八级 - 详解
  • AI教材写作神器来袭!低查重编写,轻松打造专业教材!
  • 掌握AI专著撰写技巧!借助工具轻松打造专业学术专著
  • Fast Inverse Square Root(快速平方根倒数算法)
  • 低查重“杀手级”利器!AI教材编写工具助你高效产出靠谱教材!
  • 跨平台零日间谍武器:ZeroDayRAT深度剖析——双系统入侵、全域监控与未来攻防预判
  • 2026重型货架品牌优选:用户评价好的品牌大盘点,仓库货架/仓储货架/阁楼货架/横梁货架,重型货架厂商推荐排行榜 - 品牌推荐师
  • 全新视角:AI专著写作的优势与常用工具,助你高效完成大作
  • 英语_阅读_The Desert_待读
  • 【Seedance 2.0 自分镜脚本解析引擎深度白皮书】:揭秘零代码适配影视AI工作流的3大降本核心机制
  • Seedance 2.0动态光影重绘为何越升级越卡?——揭秘2.0.3版本中被忽略的Uniform Buffer对齐缺陷与修复补丁
  • 从合规到内生安全:Linux安全基线设计逻辑与未来演进
  • 使用EmbeddingGemma-300m增强Claude的代码理解能力
  • 从需求到接口上线:XinServer 全流程拆解
  • 掌握AI专著生成技巧!实用工具分享,轻松完成学术专著创作
  • No157:AI中国故事-对话落下闳——太初历法与AI纪元:春节起源与时间计算
  • 筑牢AI安全防线:ChatGPT推出锁定模式与高风险标签,重构提示词注入与数据泄露防护体系