前言
发版前一天,遇到了一个乐观锁问题。
产生乐观锁,肯定是同一时间,存在两个或者多个事务对同一条数据进行修改。假如有事务A和事务B,先commit的事务会成功,而另外一个事务就会失败。
这里记录一下遇到的问题,以及解决方法和过程
环境
spring data jpa
使用了@version + @transactional来进行事务管理和保证数据唯一
问题
退款过程,
对接了微信,易宝,支付宝。
微信,易宝机制类似,退款成功之后,会调我们另外一个回调方法。而支付宝退款没有回调我们。
然后,退款成功,走我们自己回调,发现支付宝总是会报一个乐观锁的错,也就是存在多个事务再对同一条数据操作。而微信和易宝都是正确的。
问题来了,到底是哪里同时对一条数据操作了两次???
带着这个思路找问题。
首先,走一遍退款流程,拿到退款过程中执行的sql!(这一步最关键)
然后,我们发现确实执行了两次update,修改的是同一条数据(同一天订单)。
接着,对比两条sql,看修改的是哪一个字段? 终于,发现都修改了字段refunding(该字段用来标识退款中)。第一条修改为false,第二条修改为true。
。。。。。。
接着,该当事人(我)出马了,马上找到修改这个值的地方,果然,我在退款的时候开启了一个事务A,A中修改了refunding=true,但是没有提交事务A,又发了一个http请求1去退款,当http请求1拿到返回结果,事务A才会提交。而支付宝退款,马上调了回调方法,在回调方法里面,又将refunding修改为false。回调方法开启了另外一个事务B。事务A修改了refunding的值,还没提交;这个时候事务B又去修改了一次,最后只有一个事务成功了,另外一个失败了。。。
因为中午马上要发版本,这个bug还没有解决,涛哥和师兄都来帮忙找问题。。。。还是大佬们找bug有经验。
解决完bug,顺顺利利发了版本。QAQ
解决问题
目前解决问题办法:在事务A修改refunding值时,直接将事务提交,再去发http退款请求,解析退款结果,如果请求失败,手动事务回滚(事务A提交了,肯定就回滚不了了,需要自己修改数据)。
总结
如果存在异步调用,或者多次http请求,那么一定要注意@transactional注解的使用。并不是单纯的加上事务就行。