(一)事務的隔離級別
- 大家都知道事務有四個屬性,即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了新數據。
(二)事務的參與者及分類
在一般的事務場景中通常有以下幾個參與者:
- RM:用于管理系統數據資源,即通常意義上的數據庫服務器等。
- TP monitor:主要用于分布式事務,用來協調處理多個RM的事務處理。
- TM:事務的管理者,負責事務界定、事務上下文傳播等功能。
- 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 事務管理器起效。 |