由事物隔离级别引发的血案
2015-04-07 20:43
169 查看
今天公司的系统发现一个bug:主表记录的已还款总额和还款记录表里面的偿还金额之和不一致。看到这个问题,我的第一反应是怀疑还款的时候离线锁没生效,导致并发修改主表记录。可是经过查看日志和代码,排除了这个可能性。然后又怀疑可能是由于还款之后,修改已还款总额和还款状态时只调用了jpa的save,没有flush,导致没及时写入数据库,别的线程更新的时候不是最新数据。但再一想,发现不对,因为还款的操作是在事务之中进行的,事务结束,jpa会自动把修改写入数据库,应该不会出现这个问题。后来请来大牛帮忙分析,终于发现了这个巨坑。。。。
先说一下代码结构
其中,repayInTransaction方法会被并发调用。
也许经验比较丰富的大牛看到这段示例代码立刻就能看出问题所在。是的,正如本文的标题所说,问题的关键就在于事务隔离。spring transaction的默认隔离级别(关于隔离级别,可参考http://blog.sina.com.cn/s/blog_539d361e0100ncf6.html)是“已提交读”,也就是说,如果一个事务修改了某一行数据,但尚未提交,此时另外一个事务来读取同一行是不能读取到第一个事务的修改的。
在上面的代码中,如果第一个线程执行完repay方法,但repayInTranaction方法尚未返回,这个时候lock已经释放,但事务尚未提交,新的线程创建了新的事务,要来修改同一条主表记录。由于锁已经释放,新线程可以执行到第10行的部分,读出第一个线程已经修改但尚未提交的数据。然后第一个线程提交第一个事务,接着第二个线程提交第二个事务,悲剧就发生了。。。
补救方法比较简单,修改一下代码结构,让锁的作用范围比事务的范围更大就ok了。修改后代码如下:
先说一下代码结构
@Transactional(value = Transactional.TxType.REQUIRES_NEW) public void repayInTranaction() { repay(); } // 调用返回的时候,主表的修改才flush到数据库 private void repay() { try { lock.lock(); // 查询主表记录 // 插入还款记录,状态为待还款 // 还款 // 修改还款记录,状态为已成功 // 将该次还款金额加到主表的已还款总额,这个地方修改过后调用了save, 没有flush } catch (Exception e) { // error handling } finally { lock.unlock(); } }
其中,repayInTransaction方法会被并发调用。
也许经验比较丰富的大牛看到这段示例代码立刻就能看出问题所在。是的,正如本文的标题所说,问题的关键就在于事务隔离。spring transaction的默认隔离级别(关于隔离级别,可参考http://blog.sina.com.cn/s/blog_539d361e0100ncf6.html)是“已提交读”,也就是说,如果一个事务修改了某一行数据,但尚未提交,此时另外一个事务来读取同一行是不能读取到第一个事务的修改的。
在上面的代码中,如果第一个线程执行完repay方法,但repayInTranaction方法尚未返回,这个时候lock已经释放,但事务尚未提交,新的线程创建了新的事务,要来修改同一条主表记录。由于锁已经释放,新线程可以执行到第10行的部分,读出第一个线程已经修改但尚未提交的数据。然后第一个线程提交第一个事务,接着第二个线程提交第二个事务,悲剧就发生了。。。
补救方法比较简单,修改一下代码结构,让锁的作用范围比事务的范围更大就ok了。修改后代码如下:
public void repay() { try { lock.lock(); repayInTranaction(); } catch (Exception e) { // error handling } finally { lock.unlock(); } } @Transactional(value = Transactional.TxType.REQUIRES_NEW) public void repayInTranaction() { // 查询主表记录 // 插入还款记录,状态为待还款 // 还款 // 修改还款记录,状态为已成功 // 将该次还款金额加到主表的已还款总额,这个地方修改过后调用了save, 没有flush }
相关文章推荐
- mysql事物隔离级别
- 事物隔离级别(一)
- spring事物传播特性--数据库的隔离级别
- spring事物的七种事物传播属性行为及五种隔离级别
- 数据库中事物的隔离级别
- 【mysql】事物隔离级别
- spring 事物隔离级别和传播行为
- Mysql事物四种隔离级别
- spring事物传播性及事物隔离级别
- 事物的隔离级别 isolation
- SQL Server中事物隔离级别Read Uncommitted和with(nolock)根本就不是一个意思
- Innodb 的事物隔离级别实现原理(一)
- Spring的事物有几种方式?谈谈spring事物的隔离级别和传播行为?
- 事物、隔离级别、(悲观、乐观)锁等概念理解
- mysql事物的隔离级别
- 事物的隔离级别 isolation
- 事物隔离级别和乐观锁
- 事物隔离级别介绍
- 事物隔离级别
- Hibernate 事物隔离级别 深入探究