码迷,mamicode.com
首页 > 其他好文 > 详细

由事物隔离级别引发的血案

时间:2015-04-07 21:29:01      阅读:151      评论:0      收藏:0      [点我收藏+]

标签:

今天公司的系统发现一个bug:主表记录的已还款总额和还款记录表里面的偿还金额之和不一致。看到这个问题,我的第一反应是怀疑还款的时候离线锁没生效,导致并发修改主表记录。可是经过查看日志和代码,排除了这个可能性。然后又怀疑可能是由于还款之后,修改已还款总额和还款状态时只调用了jpa的save,没有flush,导致没及时写入数据库,别的线程更新的时候不是最新数据。但再一想,发现不对,因为还款的操作是在事务之中进行的,事务结束,jpa会自动把修改写入数据库,应该不会出现这个问题。后来请来大牛帮忙分析,终于发现了这个巨坑。。。。

先说一下代码结构

 1 @Transactional(value = Transactional.TxType.REQUIRES_NEW)
 2     public void repayInTranaction() {
 3         repay();
 4     }
 5     // 调用返回的时候,主表的修改才flush到数据库
 6 
 7     private void repay() {
 8         try {
 9             lock.lock();
10             // 查询主表记录
11             // 插入还款记录,状态为待还款
12             // 还款
13             // 修改还款记录,状态为已成功
14             // 将该次还款金额加到主表的已还款总额,这个地方修改过后调用了save, 没有flush
15         } catch (Exception e) {
16             // error handling
17         } finally {
18             lock.unlock();
19         }
20     }

其中,repayInTransaction方法会被并发调用。

也许经验比较丰富的大牛看到这段示例代码立刻就能看出问题所在。是的,正如本文的标题所说,问题的关键就在于事务隔离。spring transaction的默认隔离级别(关于隔离级别,可参考http://blog.sina.com.cn/s/blog_539d361e0100ncf6.html)是“已提交读”,也就是说,如果一个事务修改了某一行数据,但尚未提交,此时另外一个事务来读取同一行是不能读取到第一个事务的修改的。

在上面的代码中,如果第一个线程执行完repay方法,但repayInTranaction方法尚未返回,这个时候lock已经释放,但事务尚未提交,新的线程创建了新的事务,要来修改同一条主表记录。由于锁已经释放,新线程可以执行到第10行的部分,读出第一个线程已经修改但尚未提交的数据。然后第一个线程提交第一个事务,接着第二个线程提交第二个事务,悲剧就发生了。。。

补救方法比较简单,修改一下代码结构,让锁的作用范围比事务的范围更大就ok了。修改后代码如下:

 1 public void repay() {
 2         try {
 3             lock.lock();
 4             repayInTranaction();
 5         } catch (Exception e) {
 6             // error handling
 7         } finally {
 8             lock.unlock();
 9         }
10     }
11     
12     @Transactional(value = Transactional.TxType.REQUIRES_NEW)
13     public void repayInTranaction() {
14             // 查询主表记录
15             // 插入还款记录,状态为待还款
16             // 还款
17             // 修改还款记录,状态为已成功
18             // 将该次还款金额加到主表的已还款总额,这个地方修改过后调用了save, 没有flush
19     }

 

由事物隔离级别引发的血案

标签:

原文地址:http://www.cnblogs.com/segeon/p/4399354.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!