分布式锁 + 事务防重失效 —— 警惕事务粒度

分布式锁 + 事务防重失效 ------ 警惕事务粒度

最近接入了一个二方流程引擎,在生产环境中发现了审批任务重复提交成功 的问题,导致业务数据被重复写入。这个问题不仅会造成数据混乱,严重时甚至可能引发重复账务处理,后果不容忽视。

背景场景

用户在前端点击"审批通过"后,后台会提交任务并写入业务表。由于提交流程中存在耗时操作 ,用户在等待过程中多次点击按钮,结果多次提交均成功返回,后台也落了多条数据

初步以为是并发未加锁。但经排查,系统使用了 Lock4j(基于 Redisson)的分布式锁机制。任务提交方法上的处理包括:

  • 使用 @Lock4j 注解;
  • 使用 @Transactional(Propagation.REQUIRED) 注解;
  • 锁获取超时 10 秒,锁持有时间 60 秒;
  • 正常情况下,第一次提交后任务会被删除,后续提交应提示"任务不存在"。

看似逻辑完备,但仍发生了重复提交。进一步分析发现了核心原因。

问题根源:锁与事务范围不一致

submitTask 方法同时使用了分布式锁和 REQUIRED 事务注解,意味着它与外部接口共享一个大事务。

问题在于:锁在 submitTask 方法执行结束后即释放,而此时外部事务仍未提交

因此,形成如下并发场景:

  1. 第一个线程获取锁,执行 submitTask,开始事务。
  2. 方法执行结束后释放锁,但事务未提交;
  3. 第二个线程获取锁后进入 submitTask
  4. 由于前一个事务未提交,任务数据仍可查询;
  5. 后续的 updatedelete操作实际未生效(影响行数为 0),但以成功退出;
  6. 最终造成数据重复。

图示:锁释放早于事务提交

sequenceDiagram participant 用户 participant 用户 participant 应用服务 participant 数据库 用户->>应用服务: 点击"审批通过" activate 应用服务 应用服务->>应用服务: 获取分布式锁 应用服务->>数据库: 查询任务 应用服务->>数据库: 修改状态 / 删除 应用服务-->>应用服务: 释放锁(事务未提交) 用户->>应用服务: 重复点击 activate 应用服务 应用服务->>应用服务: 获取锁 应用服务->>数据库: 查询任务 (数据未提交仍存在) 应用服务->>数据库: 操作无效 (行数 = 0) 应用服务-->>用户2: 返回成功 应用服务->>数据库: 提交事务

解决方案:确保范围一致

方案一:扩大锁范围(不推荐)

将分布式锁拉到外部大事务范围,确保整个流程执行期间关键操作不被打断。

  • 优点:逻辑简单
  • 缺点:耗时操作会使锁持久不释,性能降低

方案二:简化事务,缩短耗时(治标不治本)

  • 将耗时操作移出事务范围,或改为异步
  • 有助于缩短锁+事务的同时经历时间

方案三:基于数据库的应急处理(建议)

通过数据库原子操作确保任务不可重复处理,是最稳定方案。

1. SELECT ... FOR UPDATE

查询任务时使用悲观锁,确保同一时间只有一个事务操作该行

sql 复制代码
SELECT * FROM task_table WHERE task_id = ? FOR UPDATE;
2. 判断状态 + 影响行数
sql 复制代码
UPDATE task_table SET status = 'approved' WHERE task_id = ? AND status = 'pending';

如果 affectedRows != 1,则提示任务已处理,阻止重复操作。

总结

锁控并发,事务保数据一致,两者范围不一致时最易出问题

建议:

  • 不依赖分布式锁本身保障应急性
  • 任何不可重复操作,必须在数据库层有备案
  • 状态判断 + 影响行数检查是成本最低且最稳定的方案
  • 有条件时考虑用唯一索引或幂等记录表进一步防重
相关推荐
JuiceFS2 分钟前
合合信息:基于 JuiceFS 构建统一存储,支撑 PB 级 AI 训练
运维·后端
调试人生的显微镜8 分钟前
iOS 文件深度调试实战 查看用户文件 App 沙盒 系统文件与日志全指南
后端
aiopencode10 分钟前
没有 Mac,如何上架 iOS App?跨平台团队的全流程实践指南
后端
天天摸鱼的java工程师12 分钟前
如何防止重复提交订单?
java·后端·面试
星星电灯猴15 分钟前
Charles 抓不到包怎么办?详解原因与多维解决方案
后端
慕尘_18 分钟前
对于未来技术的猜想:Manus as a Service
前端·后端
秋水丶秋水25 分钟前
Python常见异常和处理方案
后端·python
程序无bug26 分钟前
pring Boot监控方案
java·后端
荔枝爱编程27 分钟前
高性能企业级消息中心架构实现与分享(二)
后端·消息队列·rocketmq
莹莹啦28 分钟前
Java 21 核心特性全景解析:LTS 版本的革命性升级
后端