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

問題

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

排查

  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. 幸虧在壓力測試中發現這個問題,並無形成線上故障.有些問題不是作幾筆交易就能說明清楚,必須在高併發高壓力的測試下才能驗證系統的健壯性.併發

相關文章
相關標籤/搜索