分布式锁+事务=灾难?不要把锁加在事务内!!!

来源:juejin.cn/post/7484023895268278310
👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
新项目:《从零手撸:仿小红书(微服务架构)》 收尾中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍;演示地址:http://116.62.199.48:7070/
《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;
专栏阅读地址:https://www.quanxiaoha.com/column
截止目前,累计输出 90w+ 字,讲解图 3556+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有3100+小伙伴加入

用了"一锁二判三更新",但是幂等被击穿
代码案例
在我们的项目中,我们很多地方都会为了避免并发,增加分布式锁,并且会采用一锁、二判、三更新的方式实现一个幂等的逻辑。
同时,为了方便大家使用分布式锁,我们自己定义了一个@DistributeLock的直接。也是就有以下代码:
同一个方法中,增加了多个注解,同时有@DistributeLock和@Transactional两个注解。
这种情况在,我们自定义的注解@DistributeLock的切面默认会在最后执行,于是这段代码就是会先执行事务,然后再执行加锁。
最终的逻辑就像下面这段代码一样:
@Transactional(rollbackFor = Exception.class)
public boolean register(Request request) {
RLock lock = redisson.getLock(request.getIdentifier());
try {
//一锁
lock.lock();
//二查
User user = userMapper.find(request.getIdentifier());
if (user != null) {
returnfalse;
}
//三更新,保存订单数据
userMapper.insertOrder(request);
} finally {
lock.unlock();
}
returntrue;
}
按照这个顺序执行的:
进入事务
加锁
解锁
事务提交
这时候就会出现一种情况,在第三步和第四步中间,如果有一个其他的线程也调用这个 register 方法了。
那么就会出现一个问题,锁已经释放了,但是事务还没提交。这时候其他的线程在并发请求过来的时候:
一锁: 拿锁可以拿到,因为锁被释放了
二查: 查询数据也查不到,因为这时候之前的那个事务可能还没提交,未提交的数据,新的事务是看不到的。
三更新: 执行更新操作,导致数据重复或者报错。
这就是我们需要解决的问题,那么看上去就是事务的切面执行顺序的问题,我们应该让锁的粒度大于事务的粒度就能解决了这个问题了。
那么,就想办法让分布式锁的注解的切面先执行。解决办法就是借助@Order注解,他可以直接用在切面类上,用于指定切面的执行顺序。值越小,优先级越高,切面会越早执行。
所以,修改后的分布式锁的切面类如下:
新增 Order 注解,把他的优先级设置为最小值,即优先级最高,最先开始执行即可。
总结
在使用分布式锁的时候,习惯性的尽量缩小同步代码块的范围
但是如果数据库隔离级别是可重复读,这种情况下不要把分布式锁加在@Transactional注解的事务方法内部。
因为可能会出现这种情况:
线程1开启事务A后获取分布式锁,执行业务代码后在事务内释放了分布式锁。这时候线程1开启了事务B获取到了线程1释放的分布式锁,执行查询操作时查到的数据就可能出现问题。因为此时事务A是在事务内释放了锁,事务A本身还没有完成提交。
👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
新项目:《从零手撸:仿小红书(微服务架构)》 收尾中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍;演示地址:http://116.62.199.48:7070/
《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;
专栏阅读地址:https://www.quanxiaoha.com/column
截止目前,累计输出 90w+ 字,讲解图 3556+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有3100+小伙伴加入


1. 我的私密学习小圈子,从0到1手撸企业实战项目~ 2. 高并发下的计数器,为什么阿里不推荐使用 AtomicLong? 3. @Transactional + @Async 有大坑! 4. 闲置安卓机逆袭,手把手教你零成本打造私人服务器!
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。 获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。 点“在看”支持小哈呀,谢谢啦
更多推荐

所有评论(0)