Spring事務那些事兒

(一)事務的隔離級別

  • 大家都知道事務有四個屬性,即ACID(原子性、一致性、隔離性、持久性)。這四個裏面稍微難理解點的是一致性和持久性。所謂的一致性是指:事務執行前後數據的一致性狀態,例如事務執行前用戶有1萬元,事務回滾後用戶仍應該有1萬元。而這裏的持久性指的是:事務在提交後結果是永久的,即使程序崩潰數據也能恢復,當然數據庫的持久性指的是高可靠性,事務執行後數據保證一定寫到了磁盤及備份日誌,但並不是指高可用性 ,如果你強行把機器的磁盤燒了,那數據也是無法恢復的。
  • 在事務隔離性的基礎上,我們對事務間的相互影響程度進行定級,即事務的隔離級別。通常事務有四種隔離級別:read uncommitted、read committed、repeatable read、serializable。
    (1)read uncommitted是最低的隔離級別,其實就如其英文名一樣,處於這個級別的事務可能會讀到其他事務尚未提交的數據修改,即存在髒讀問題。除此之外,還存在不可重複讀和幻讀問題問題。
    (2)read committed是大部分數據庫的默認隔離級別,它解決了髒讀問題,但是仍存在不可重複讀和幻讀問題。
    (3)repeatable read解決了不可重複讀的問題,但是仍可能存在幻讀的問題。值得一提的是mysql的默認級別是repeatable read,但它卻不存在幻讀問題,因爲mysql通過使用next-key lock技術解決了問題,即使在repeatable read級別也能完全保證事務的隔離性要求。
    (4) serializable是最爲嚴格的隔離級別,所有事務順序執行,實際上是通過將並行轉爲串行來解決事務隔離問題,無疑會降低性能。

不可重複讀和幻讀的區別:
不可重複讀指在同一事務中多次查詢同一記錄(eg:select * from xx where id = 1),查詢結果不一致。這主要是由於多次讀期間其他事務update或delete了記錄。
幻讀是指在同一事務中多次進行範圍查詢(eg:select count(*) from xx where id > 1 and id < 100),查詢結果不一致。這主要是由於多次範圍查詢期間其他事務insert了新數據。

(二)事務的參與者及分類

在一般的事務場景中通常有以下幾個參與者:

  1. RM:用於管理系統數據資源,即通常意義上的數據庫服務器等。
  2. TP monitor:主要用於分佈式事務,用來協調處理多個RM的事務處理。
  3. TM:事務的管理者,負責事務界定、事務上下文傳播等功能。
  4. Applacation:運行於容器中的應用程序,例如spring應用程序。

我們通常會根據RM的數量將事務分爲分佈式事務和局部事務(本地事務)兩種。 在分佈式事務中通常會存在多個不同的RM,這些RM分佈在不同的系統中,相互之間通過TP monitor協調,利用兩階段提交來保障事務的ACID屬性。在局部事務中一般每次數據操作只有一個RM,數據操作只需操作一個數據庫,在同一個事務中不會進行多個數據庫的更新。接下來我們將着重介紹局部事務的實現,畢竟分佈式事務我懂的也不多😜。

(三)局部事務詳解

1.事務原理簡介

首先我們需要了解的很重要的一點是,spring的事務底層實現本質上是依賴於其使用的數據庫服務器,例如mysql數據庫。在mysql數據庫中事務的隔離依賴於mysql的鎖機制,持久性和原子依賴於redo日誌,而一致性依賴於undo日誌。mysql提供事務功能的具體實現,spring負責界定事務的邊界及定義事務的傳播性,然後通過特定的數據訪問技術(例:jdbc、hibernate等)的API對事務進行管理。
我們以jdbc舉個簡單的例子,我們可以看下面的代碼:

        Connection connection = null;
        boolean rollback = false;
        try {
            connection = dataSource.getConnection();
            connection.setAutoCommit(false);
            // 數訪問
            XXXXXXXXXXXX;
            connection.commit();
        } catch (SqlException e) {
            rollback = true;
        } finally {
            if (rollback) {
                connection.rollback();
            } else {
                connection.close();
            }
        }

這就是我們利用jdbc進行的最簡單的事務管理,將autoCommit置爲false,然後根據各種情況進行事務的回滾提交等操作。而我們平時普通的數據操作都是默認自動提交,mysql會自動隱式的幫我們commit事務。
但是上面的寫法在實際的開發中存在很多的問題:1.不同的數數據訪問技術有不同的api、2.我們需要自己手動捕獲處理各種以sql異常、3.事務管理代碼和數據訪問代碼及業務代碼耦合在一起。

2.spring事務框架重要類

爲了解決上面提到的問題,spring爲我們提供了優秀的事務框架。我們先來介紹下事務框架中重要的類:

頂層接口 職責
TransactionDefinition 用來定義事務屬性,包括事務的隔離性、傳播性、超時時間、是否只讀等屬性。
TransactionStatus 用來記錄事務開啓到事務結束期間事務的狀態,這個類一般在編程式事務中使用。
PlatformTransactionManager spring事務框架的核心類,負責事務的管理,包括事務的commit、rollback等。
3.spring事務傳播性
事務傳播性 說明
REQUIRED 業務方法需要在一個事務中運行,如果方法運行時,已處在一個事務中,那麼就加入該事務,否則自己創建一個新的事務,這是spring默認的傳播行爲。
SUPPORTS 如果業務方法在某個事務範圍內被調用,則方法成爲該事務的一部分,如果業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行。
MANDATORY 只能在一個已存在事務中執行,業務方法不能發起自己的事務,如果業務方法在沒有事務的環境下調用,就拋異常。
REQUIRES_NEW 業務方法總是會爲自己發起一個新的事務,如果方法已運行在一個事務中,則原有事務被掛起,新的事務被創建,直到方法結束,新事務才結束,原先的事務纔會恢復執行。
NOT_SUPPORTED 聲明方法需要事務,如果方法沒有關聯到一個事務,容器不會爲它開啓事務。如果方法在一個事務中被調用,該事務會被掛起,在方法調用結束後,原先的事務便會恢復執行。
NEVER 聲明方法絕對不能在事務範圍內執行,如果方法在某個事務範圍內執行,容器就拋異常。只有沒關聯到事務,才正常執行。
NESTED 如果一個活動的事務存在,則運行在一個嵌套的事務中。如果沒有活動的事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個可以回滾的保證點。內部事務回滾不會對外部事務造成影響, 它只對DataSourceTransactionManager 事務管理器起效。

--------未完待續,敬請期待

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章