线上问题复盘:分布式锁 + @Transactional 组合失效?
摘要本文深入剖析了 Spring Boot 项目中分布式锁与 Transactional 事务组合使用时因锁提前释放而导致的并发数据不一致问题。通过对比问题版本与修复版本的时序差异揭示了 Spring AOP 执行顺序的陷阱并提供了两种有效的解决方案服务层拆分与编程式事务帮助开发者避免类似的高并发坑。一问题场景用户并发操作某个资源比如领取限量优惠券或者高频修改某个配置。这种防并发超卖的场景老 Java 开发闭着眼睛都能想到一套组合拳Spring Boot Transactional Redisson 分布式锁。当时同事咔咔一顿敲代码写得极其丝滑本地单线程测了没毛病经过 CodeReview 后直接提测上线。结果上线第一天运营跑过来说“后台数据不对啊怎么同一个资源被重复扣了两次”同事当时赶紧去看日志。不看不知道一看麻了虽然加了分布式锁但在极高并发下锁竟然“失效”了今天给大家复盘一下这个差点让同事背绩效 C 的坑也提醒大家 CodeReview 时多关注这类时序问题。二代码复现为了直观我把当时的业务代码简化一下。大体逻辑是这样的查询数据库中的剩余量 - 判断是否足够 - 扣减 - 保存。Java 代码示例问题版本Service public class ResourceServiceImpl implements ResourceService { Autowired private RedissonClient redissonClient; Autowired private ResourceMapper resourceMapper; Override Transactional(rollbackFor Exception.class) public void consumeResource(String resourceId) { String lockKey lock:resource: resourceId; RLock lock redissonClient.getLock(lockKey); try { // 尝试获取锁最多等3秒 if (lock.tryLock(3, TimeUnit.SECONDS)) { // 1. 查数据库当前库存 Resource res resourceMapper.selectById(resourceId); if (res.getStock() 0) { // 2. 扣减 res.setStock(res.getStock() - 1); resourceMapper.updateById(res); } else { throw new RuntimeException(库存不足); } } else { throw new RuntimeException(系统繁忙请重试); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 3. 释放锁 if (lock.isLocked() lock.isHeldByCurrentThread()) { lock.unlock(); } } } }可以直接看下这代码是不是很眼熟Transactional保证事务try...finally保证锁必定释放。这代码经过 CodeReview 时逻辑上看起来都没毛病啊但是并发一上来数据库的库存还是出现了负数。三问题分析-锁释放与事务提交的时差为了更直观地理解这个问题下面通过时序图对比问题版本和修复版本的关键执行顺序从上图可以清晰地看到问题版本线程A在第5步就释放了锁但事务提交在第9步。线程B在第6-8步拿到锁并操作数据库时看到的是未提交的老数据导致脏写。修复版本线程A在第5步先提交事务然后在第6步释放锁。线程B必须等待锁释放后才能操作看到的是已提交的新数据。排查了大半天抓了 MySQL 的 binlog 和 Redis 的执行日志对比终于发现了问题所在Spring AOP 的执行顺序问题。大家回忆一下Transactional的底层原理。Spring 是通过 AOP 动态代理来实现事务的相当于在你的业务方法外层包了一层Spring 代理类的伪代码// Spring 代理类的伪代码 public void proxyConsumeResource(String resourceId) { // 1. 开启数据库事务 connection.setAutoCommit(false); try { // 2. 执行你的真实业务逻辑包含加锁、改数据、释放锁 target.consumeResource(resourceId); // 3. 提交事务 connection.commit(); } catch (Exception e) { connection.rollback(); } }看出致命问题了吗在我的业务代码里finally块执行了lock.unlock()此时分布式锁已经被释放了。但是此时target.consumeResource()方法才刚刚执行完Spring 代理类的connection.commit()还没执行也就是说锁已经没了但数据还没落盘。这时候如果有另一个线程线程B并发打进来线程B看到 Redis 里没有锁顺利拿到锁。线程B去查数据库。因为线程A的事务还没 commit线程B查到的还是老数据线程B拿着老数据做扣减。线程A提交事务线程B紧接着也提交事务。完美脏写产生了数据被彻底打穿。四解决方案找到原因后解决起来就非常简单了。核心思想只有一个必须保证事务提交之后再释放锁。方案一粗暴拆分直接把加锁的逻辑往上提放到 controller 层或者再包一层 service。确保锁的范围大于事务的范围。Java 代码示例修复版本Service public class ResourceLockService { Autowired private RedissonClient redissonClient; Autowired private ResourceServiceImpl resourceService; // 注入原来的事务Service public void safeConsume(String resourceId) { String lockKey lock:resource: resourceId; RLock lock redissonClient.getLock(lockKey); try { if (lock.tryLock(3, TimeUnit.SECONDS)) { // 调用事务方法由于事务方法是一个独立的 proxy // 执行完毕返回到这里时事务已经 commit 啦 resourceService.consumeResource(resourceId); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (lock.isLocked() lock.isHeldByCurrentThread()) { lock.unlock(); } } } }注注意不要在同一个类里写这两个方法直接 this 调用会导致 AOP 失效老生常谈了。方案二编程式事务如果你不想多写一层类可以使用TransactionTemplate手动控制事务的边界。Java 代码示例// 在获取锁之后执行 transactionTemplate.execute(status - { // 查库、扣减、更新 return null; }); // 事务提交完毕再进入 finally 释放锁其它问题Redisson 看门狗失效问题借着这个机会再分享一个很多人用 Redisson 容易踩的坑。有些喜欢直接使用vibe coding会在加锁的时候传 leaseTime锁过期时间错误示例// 试图加锁等待3秒锁定10秒后自动释放 lock.tryLock(3, 10, TimeUnit.SECONDS);一旦你显式传入了leaseTimeRedisson 的 WatchDog看门狗机制就会失效如果你的业务逻辑执行时间超过了 10 秒比如发生了 Full GC 或者调了很慢的第三方接口锁会自动释放其他线程就会趁虚而入。正确做法是如果不知道业务具体执行多久千万别传leaseTime正确示例// 只传等待时间不传 leaseTime看门狗机制生效会自动帮你续期 lock.tryLock(3, TimeUnit.SECONDS);总结与建议平时的业务开发中Transactional和各种锁包括本地锁synchronized和分布式锁一起用的时候一定要多留个心眼画一画它们的作用域边界。核心要点回顾事务与锁的顺序确保锁的生命周期完全覆盖事务的生命周期即先加锁后开启事务先提交事务后释放锁。方案选择优先推荐方案一粗暴拆分逻辑清晰职责分离。方案二编程式事务适合简单场景。Redisson 使用避免随意指定leaseTime充分利用看门狗自动续期机制。测试验证高并发场景务必进行压测并结合日志和监控观察锁与事务的时序。有时候真不是底层组件不行纯粹是我们没把 Spring AOP 的执行顺序盘明白。希望这次的血泪教训能帮大家少掉几根头发。大家在线上还踩过什么离谱的并发问题欢迎评论区交流

相关新闻

LaserGRBL:当激光雕刻遇到开源智慧,你的创意如何被完美实现?

LaserGRBL:当激光雕刻遇到开源智慧,你的创意如何被完美实现?

LaserGRBL:当激光雕刻遇到开源智慧,你的创意如何被完美实现? 【免费下载链接】LaserGRBL Laser optimized GUI for GRBL 项目地址: https://gitcode.com/gh_mirrors/la/LaserGRBL 你是否曾经遇到过这样的困扰?手里有一台激…

2026/8/1 20:58:37 阅读更多 →
基于 OpenLayers 实现态势地图手动标绘航线、作战区域及点位悬浮弹窗

基于 OpenLayers 实现态势地图手动标绘航线、作战区域及点位悬浮弹窗

需求:基于 OpenLayers 技术框架实现地图交互效果,支持操作人员在地图上手动绘制飞机运动轨迹、舰船航行轨迹以及作战区域范围;当鼠标光标移动悬浮至飞机、舰船点位之上时,页面弹出悬浮信息面板,展示当前对应装备的详细…

2026/8/2 22:02:17 阅读更多 →
C++进阶(11):C++ IO 流

C++进阶(11):C++ IO 流

💬 :如果你在阅读过程中有任何疑问或想要进一步探讨的内容,欢迎在评论区畅所欲言!我们一起学习、共同成长~!👍 :如果你觉得这篇文章还不错,不妨顺手点个赞、加入收藏,并分…

2026/8/2 22:50:21 阅读更多 →

最新新闻

Hidden Markov Model在Barefoot中的应用:揭秘地图匹配背后的概率模型

Hidden Markov Model在Barefoot中的应用:揭秘地图匹配背后的概率模型

Hidden Markov Model在Barefoot中的应用:揭秘地图匹配背后的概率模型 【免费下载链接】barefoot Java map matching library for integrating the map into software and services with state-of-the-art online and offline map matching that can be used stand-a…

2026/8/2 22:50:58 阅读更多 →
Django多表全文搜索实战:Haystack+Jieba+Whoosh集成与API设计

Django多表全文搜索实战:Haystack+Jieba+Whoosh集成与API设计

1. 项目概述与核心痛点最近在重构一个老旧的内部管理系统,后端用的是Django 2.2.7,前端是Vue,典型的分离架构。其中一个核心需求是全文搜索,涉及多个模型表,比如文章、用户、产品信息。技术栈选型上,我沿用…

2026/8/2 22:49:57 阅读更多 →
SharpXDecrypt:三分钟找回Xshell遗忘密码的专业解决方案

SharpXDecrypt:三分钟找回Xshell遗忘密码的专业解决方案

SharpXDecrypt:三分钟找回Xshell遗忘密码的专业解决方案 【免费下载链接】SharpXDecrypt Xshell全版本密码恢复工具 项目地址: https://gitcode.com/gh_mirrors/sh/SharpXDecrypt 你是否曾因忘记Xshell保存的服务器密码而陷入困境?当紧急需要登录…

2026/8/2 22:49:57 阅读更多 →
I2C从设备模拟:ESP32-Bit-Pirate伪装传感器与EEPROM的完整指南

I2C从设备模拟:ESP32-Bit-Pirate伪装传感器与EEPROM的完整指南

I2C从设备模拟:ESP32-Bit-Pirate伪装传感器与EEPROM的完整指南 【免费下载链接】ESP32-Bit-Pirate A Hardware Hacking Tool with Web-Based CLI That Speaks Every Protocol 项目地址: https://gitcode.com/GitHub_Trending/es/ESP32-Bit-Pirate ESP32-Bit…

2026/8/2 22:49:57 阅读更多 →
解锁3DS游戏新玩法:Mandarine-NEO独家特性与 hacks 完全解析

解锁3DS游戏新玩法:Mandarine-NEO独家特性与 hacks 完全解析

解锁3DS游戏新玩法:Mandarine-NEO独家特性与 hacks 完全解析 【免费下载链接】mandarine-neo A Citra fork with new features and hacks which are experimental and may not be present in Azahar. Continuation of Mandarine 3DS emulator. 项目地址: https://…

2026/8/2 22:49:57 阅读更多 →
OpenVR高级设置:终极SteamVR性能优化工具,轻松提升虚拟现实体验

OpenVR高级设置:终极SteamVR性能优化工具,轻松提升虚拟现实体验

OpenVR高级设置:终极SteamVR性能优化工具,轻松提升虚拟现实体验 【免费下载链接】OpenVR-AdvancedSettings OpenVR Advanced Settings Dashboard Overlay 项目地址: https://gitcode.com/gh_mirrors/op/OpenVR-AdvancedSettings 你是否曾经在Stea…

2026/8/2 22:49:57 阅读更多 →

日新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/2 6:34:16 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/2 2:47:48 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/2 0:23:22 阅读更多 →