一次野事務排查

壹頁書發表於2016-11-09
測試環境出了一個很神奇的問題。
某一個事務鎖了很多不同表的不同記錄(暫且先叫他野事務),導致其他事務都阻塞了.
這個野事務往往持續數個小時,並不提交或者回滾,就生生的佔在那裡。
野事務鎖的表,相互之間沒有任何的關聯.不能定位到某一個業務或者模組.

經過排查,這個問題和我們的開發方式有關係.
雖然使用了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((short0);  
  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.         }  

在第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部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2128120/,如需轉載,請註明出處,否則將追究法律責任。

相關文章