更多大厂面试内容可见 -> http://11come.cn
听我的,事务注解真的别乱动!
背景
故事的起源: 发现存在重复插入数据库的现象,通过排查发现是因为事务中包了锁
原因分析: 当线程 1 释放锁之后,但是此时还未提交事务,线程 2 此时抢到锁,去执行数据库插入数据,此时由于线程 1 的事务未提交,线程 2 读取不到新插入的数据,所以会插入重复数据
这里在方法 1 和方法 2 上都有事务注解,根据默认的事务传播机制,方法 2 会加入到方法 1 的事务中,导致方法 2 执行完毕之后,要过一段时间才会提交整个事务,在这一段时间内,就会重复插入数据
而且由于业务原因,嵌套层数太多,无法将加锁逻辑移动到事务外,因此考虑将方法 2 的事务传播级别设置为 REQUIRES_NEW
,在方法 2 上开启一个新的事务,尽快提交插入的数据,如下图:
代码改动如下
// 原代码
@Transactional// 新代码
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
现象
事务传播机制改动为 REQUIRES_NEW
上线之后,数据出现问题,表现形式为部分数据未生成
经排查之后,定位到是这个注解的问题,将注解修改回滚就不会出现这种情况了
原因
这里修改了事务的传播机制,修改为了 REQUIRES_NEW
,而默认的传播机制为 PROPAGATION_REQUIRED
REQUIRES_NEW
传播机制会新建事务,无论当前是否存在事务,都会创建新的事务,新建的事务将和外层的事务没有任何关系,是两个独立的事务
那么这样的话,事务 2 确实可以更快地进行提交了,但是会导致他和事务 1 之间的数据隔离开来
而恰巧这里在事务 1 中插入了部分数据,在事务 2 中需要获取这些数据 ,但是由于两个事务隔离开,在事务 2 中读取不到事务 1 中未提交的数据,从而出现线上问题
思考
涉及到事务相关,思考两个点:
- 通过加锁避免数据重复添加,但是如果外层存在事务,那么数据添加就不会立即生效,还是会存在重复添加数据的情况
- 如果将事务的隔离级别由默认改为
REQUIRES_NEW
,会形成独立的事务,如果上层事务插入数据,该事务会读取不到,存在数据隔离的情况
因此,如果涉及到事务传播级别的修改,要考虑不同事务之间数据独立的问题!
可能解决的方案
自己想到一个解决的方案,可以通过编程式事务来解决,手动控制事务开始和提交,可以对事务的覆盖范围较清晰,减少出现问题的概率