问题背景:

账单结算完成时需要更新对应账户余额。如果多笔账单同时结算完成,此时会同时更新账户余额。更新的步骤为,先查询账户当前余额,然后累加上本次账单金额,然后更新。例如,账单1和账单2同时查到当前账户余额为10元,其中账单1金伟为5元,于是先将余额更新为10+5=15元并提交事务,账单2金额为8元,并将余额更新为10+8=18元也提交事务。最后账户余额为18元。

检查事务隔离级别为可重复读。数据库为mysql。

 

 

 

排查原因:

mysql数据库,通过设置事务隔离级别为可重复读(REPEATABLE_READ)无法避免发生“第二类丢失更新”问题。(问题参见:https://www.it1352.com/1667833.html)

 

第二类丢失更新:

它和不可重复读本质上是同一类并发问题,通常将它看成不可重复读的特例。当两个或多个事务查询相同的记录,然后各自基于查询的结果更新记录时会造成第二类丢失更新问题。每个事务不知道其它事务的存在,最后一个事务对记录所做的更改将覆盖其它事务之前对该记录所做的更改。

 

解决方案:

A. 悲观锁方案:

1. 开启事务的方法执行之前先加锁(分布式环境需要加分布式锁)。特别注意:在事务方法内部的某个步骤加锁,是无效的,因为这一步骤执行完毕释放锁后,当前事务仍未提交,若在此期间另一个事务B已经开始执行,那么事务B仍然会拿到之前提交的数据。

 

B. 乐观锁方案:

1. 执行更新SQL语句时,条件中增加对数据版本的判断,必须是对查询到的那个版本的数据进行更新,且update语句执行完毕后检查更新行数,如果更新时版本号已发生改变,此时update语句返回的行数肯定预期的行数,此时可认定为查询后更新前这段时间数据已发生改变,抛出异常,然后重试