测试环境出了一个很神奇的问题。
某一个事务锁了很多不同表的不同记录(暂且先叫他野事务),导致其他事务都阻塞了.
这个野事务往往持续数个小时,并不提交或者回滚,就生生的占在那里。
野事务锁的表,相互之间没有任何的关联.不能定位到某一个业务或者模块.
经过排查,这个问题和我们的开发方式有关系.
虽然使用了Spring,但是居然没有使用Spring的事务管理机制.
绝大多数的数据库操作,都是依赖于自动提交.另外少数的模块,使用了手动开启的编程式事务管理.
但是编程式事务的一个分支判断出现了bug,没有提交事务或者回滚.
导致了这个连接归还连接池之后(这时候这个连接 autocommit尚在false状态)
其他自动提交的模块如果使用这个连接执行,那么就会不断的加锁,而不释放.从而导致一系列怪异神奇的现象.
当时出问题的代码示例如下
-
public Map<String, Object> xxxMethod(String jsonParam) {
-
BaseParam baseParam = JsonUtil.getObjectFromJson(jsonParam, BaseParam.class);
-
Map<String, Object> bizParamMap = baseParam.getBizParamMap();
-
-
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
-
TransactionStatus status = transactionManager_vvlivemetadata.getTransaction(def);
-
try {
-
UserBaseinfo userBaseinfo = userBaseinfoMapper.selectByPrimaryKey(lineUserID);
-
if (userBaseinfo == null) {
-
return createFailResult(baseParam, RetEnum.RET_METADATA_USER_NOT_EXISTS);
-
}
-
LiveLineInfo liveLineInfo = new LiveLineInfo();
-
liveLineInfo.setLiveLineID(lineUserList.get(0).getLiveLineID());
-
liveLineInfo.setLiveID(liveID);
-
liveLineInfo.setUserID(userID);
-
liveLineInfo.setLineUserID(lineUserID);
-
liveLineInfo.setStatus((short) 0);
-
int liveLineInfoRetNum = liveLineInfoMapper.updateByPrimaryKeySelective(liveLineInfo);
-
if (liveLineInfoRetNum != 1) {
-
transactionManager_vvlivemetadata.rollback(status);
-
_log.error("取消连麦失败, 房间上麦信息状态修改失败. jsonParam={}, liveLineInfoRetNum={}",
-
jsonParam, liveLineInfoRetNum);
-
return createFailResult(baseParam, RetEnum.RET_DB_FAIL);
-
}
-
liveLineInfoMapper.updateOthersShowPosBatch(liveID, lineUserList.get(0).getShowPos());
-
transactionManager_vvlivemetadata.commit(status);
-
return createBizResult(baseParam, jsonResult);
-
} catch (Exception e) {
-
transactionManager_vvlivemetadata.rollback(status);
-
return createFailResult(baseParam, RetEnum.RET_DB_FAIL);
-
}
-
}
在第10行的代码,未结束事务而直接返回,是造成这个问题的原因.
开发同事之所以不使用spring的事务管理,是因为如果要spring回滚事务,需要抛出异常,而他们还想返回一个业务上的状态值.
比如
return createFailResult(baseParam, RetEnum.RET_METADATA_USER_NOT_EXISTS);
其实我觉得不如使用自定义异常,继承RuntimeException,然后增加一个业务状态的数据成员.
经过这次排查,小有收获,也纠正了自己的一个错误认识。
原来一直以为 mysql
start transaction 是 set autocommit=false 的同义词.
但是两种方式,还有差别.
-
set autocommit=false;
-
update t set str='edmond' where id=1;
-
-
start transaction;
但是set autocommit=false不会提交之前的事务.
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29254281/viewspace-2128120/
,如需转载,请注明出处,否则将追究法律责任。