ITPub博客

首页 > 应用开发 > Java > 一次野事务排查

一次野事务排查

原创 Java 作者:壹頁書 时间:2016-11-09 10:51:17 1 删除 编辑
测试环境出了一个很神奇的问题。
某一个事务锁了很多不同表的不同记录(暂且先叫他野事务),导致其他事务都阻塞了.
这个野事务往往持续数个小时,并不提交或者回滚,就生生的占在那里。
野事务锁的表,相互之间没有任何的关联.不能定位到某一个业务或者模块.

经过排查,这个问题和我们的开发方式有关系.
虽然使用了Spring,但是居然没有使用Spring的事务管理机制.
绝大多数的数据库操作,都是依赖于自动提交.另外少数的模块,使用了手动开启的编程式事务管理.
但是编程式事务的一个分支判断出现了bug,没有提交事务或者回滚.
导致了这个连接归还连接池之后(这时候这个连接 autocommit尚在false状态)
其他自动提交的模块如果使用这个连接执行,那么就会不断的加锁,而不释放.从而导致一系列怪异神奇的现象.

当时出问题的代码示例如下
  1. public Map<String, Object> xxxMethod(String jsonParam) {  
  2.         BaseParam baseParam = JsonUtil.getObjectFromJson(jsonParam, BaseParam.class);  
  3.         Map<String, Object> bizParamMap = baseParam.getBizParamMap();  
  4.   
  5.         DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
  6.         TransactionStatus status = transactionManager_vvlivemetadata.getTransaction(def);  
  7.         try {  
  8.             UserBaseinfo userBaseinfo = userBaseinfoMapper.selectByPrimaryKey(lineUserID);  
  9.             if (userBaseinfo == null) {  
  10.                 return createFailResult(baseParam, RetEnum.RET_METADATA_USER_NOT_EXISTS);  
  11.             }  
  12.             LiveLineInfo liveLineInfo = new LiveLineInfo();  
  13.             liveLineInfo.setLiveLineID(lineUserList.get(0).getLiveLineID());  
  14.             liveLineInfo.setLiveID(liveID);  
  15.             liveLineInfo.setUserID(userID);  
  16.             liveLineInfo.setLineUserID(lineUserID);  
  17.             liveLineInfo.setStatus((short) 0);  
  18.             int liveLineInfoRetNum = liveLineInfoMapper.updateByPrimaryKeySelective(liveLineInfo);  
  19.             if (liveLineInfoRetNum != 1) {  
  20.                 transactionManager_vvlivemetadata.rollback(status);// bug  当时没加  
  21.                 _log.error("取消连麦失败, 房间上麦信息状态修改失败. jsonParam={}, liveLineInfoRetNum={}",  
  22.                         jsonParam, liveLineInfoRetNum);  
  23.                 return createFailResult(baseParam, RetEnum.RET_DB_FAIL);  
  24.             }  
  25.             liveLineInfoMapper.updateOthersShowPosBatch(liveID, lineUserList.get(0).getShowPos());  
  26.             transactionManager_vvlivemetadata.commit(status);  
  27.             return createBizResult(baseParam, jsonResult);  
  28.         } catch (Exception e) {  
  29.             transactionManager_vvlivemetadata.rollback(status);  
  30.             return createFailResult(baseParam, RetEnum.RET_DB_FAIL);  
  31.         }  
  32. } 

在第10行的代码,未结束事务而直接返回,是造成这个问题的原因.
开发同事之所以不使用spring的事务管理,是因为如果要spring回滚事务,需要抛出异常,而他们还想返回一个业务上的状态值.
比如
return createFailResult(baseParam, RetEnum.RET_METADATA_USER_NOT_EXISTS);

其实我觉得不如使用自定义异常,继承RuntimeException,然后增加一个业务状态的数据成员.

经过这次排查,小有收获,也纠正了自己的一个错误认识。
原来一直以为 mysql
start transaction 是 set autocommit=false 的同义词.
但是两种方式,还有差别.

  1. set autocommit=false;  
  2. update t set str='edmond' where id=1;  
  3. -- start transaction会提交之前的事务  
  4. start transaction; 

但是set autocommit=false不会提交之前的事务.

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29254281/viewspace-2128120/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: 简易LruMap
请登录后发表评论 登录
全部评论

注册时间:2013-10-19

  • 博文量
    620
  • 访问量
    6339923