1. 程式人生 > 程式設計 >記一次Spring @Transactional失效的排查過程

記一次Spring @Transactional失效的排查過程

問題

壓力測試時發現生成了相同的序號,根據日誌分析發現select ... for update沒有鎖住某一行的資料,從而導致序號重複

排查

  1. 十分確定select ... for update的互斥機制,該行資料必定被鎖,其他select ... for update/update/delete等SQL需要等待鎖才能執行後續操作.
  2. 首先懷疑事務是否起作用,使用TransactionSynchronizationManager#getCurrentTransactionName列印當前的事務,結果竟然是null!!!沒有事務自然不會互斥,能解釋為什麼生成重複序號了.
  3. 剩下就是為什麼明明加了@Transactional註解,但是沒有開啟事務了.
  4. 帶著問題檢查程式碼,有問題的程式碼如下圖.這是一個責任鏈的設計模式,對於責任鏈的任一個節點,先呼叫doProcess執行子類自身的邏輯,如果成功則執行下一個succ節點,報錯則執行下一個fail節點,直到責任鏈結束.
  5. 找到了宣告事務的doProcess方法,他是在process裡用this.doProcess調起的,我們都知道Spring的AOP事務要生效,必須使用 另一物件.方法 的形式才有效,這就是@Transactional失效的原因. 簡而言之,不能在同一個類中使用沒有@Transactional的方法調起有@Transactional的方法
public abstract class
BaseController
{ private ScBaseController succNext = null; private ScBaseController failNext = null; public DataMessage process(DataMessage msg) throws ScBaseException { DataMessage respMsg = this.doProcess(msg); // 執行成功 if (ExecuteResultEnum.SUCCESS.equals(respMsg.getExecuteResult())) { if
(this.succNext != null) { return this.succNext.process(respMsg); } else { return respMsg; } } // 執行失敗 else { if (this.failNext != null) { return this.failNext.process(respMsg); } else { return respMsg; } } } @Transactional // 這裡的事務註解沒有作用 public abstract DataMessage doProcess(DataMessage msg); 複製程式碼

教訓

  1. 看過很多文章都說過不能在同一個類中使用沒有@Transactional的方法調起有@Transactional的方法,這個是Spring動態AOP的機制決定的,必須緊記這個教訓.至於為什麼動態AOP會這樣,後續如果寫到這方面的文章再詳細說明.

  2. 幸好在壓力測試中發現這個問題,並沒有造成線上故障.有些問題不是做幾筆交易就能說明清楚,必須在高併發高壓力的測試下才能驗證系統的健壯性.