什麼是事務

▲ 百度百科

概括來講,事務是一個由有限操作集合組成的邏輯單元。事務操作包含兩個目的,資料一致以及操作隔離。資料一致是指事務提交時保證事務內的所有操作都成功完成,並且更改永久生效;事務回滾時,保證能夠恢復到事務執行之前的狀態。操作隔離則是指多個同時執行的事務之間應該相互獨立,互不影響。

靚仔和小明的銀行賬戶都有1000元,靚仔要給小明轉賬500元。

這個過程看似簡單,實際上涉及了一系列的資料庫操作,可以簡單地視為兩步基本操作,即從靚仔帳戶的金額中扣除500元,以及將小明帳戶中金額新增500元。

假設第一步資料庫操作成功,第二步失敗了,那就會出現小明賬戶金額沒變,靚仔卻白白少了500元的情況,靚仔豈不是很悲催。

事務機制可以避免此類情況,以保證整個操作的完成,如果某步操作出錯,之前所做的資料庫操作將全部失效。

事務的特性

提到事務,不可避免需要涉及到事務的四個特性(ACID):

  • 原子性(Atomicity):事務作為一個整體被執行,包含在其中的對資料庫的操作要麼全部被執行,要麼都不執行。
  • 一致性(Consistency):事務應確保資料庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是資料庫中的資料應滿足完整性約束。
  • 隔離性(Isolation):多個事務併發執行時,一個事務的執行不應影響其他事務的執行。
  • 永續性(Durability):已被提交的事務對資料庫的修改應該永久儲存在資料庫中。

我們將嚴格遵循 ACID 屬性的事務稱為剛性事務。與之相對,期望最終一致性,在事務執行的中間狀態允許暫時不遵循 ACID 屬性的事務稱為柔性事務,柔性事務的使用涉及到分散式事務方案,後續將為大家擴充套件,這裡就不過多闡述。

Spring 事務管理的方式

1、程式設計式事務管理

程式設計式事務管理是侵入性事務管理,可通過 PlatformTransactionManager 實現來進行事務管理,Spring 提供了模板類 TransactionTemplate 進行事務管理。

2、宣告式事務管理

宣告式事務管理建立在 AOP 之上,有兩種常用的方式,一是基於 tx 和 aop 名稱空間的 xml 配置檔案,一是基於 @Transactional 註解,隨著 Spring 和 Java 的版本越來越高,越趨向於使用註解的方式。

其本質是對方法前後進行攔截,然後在目標方法開始之前建立或者加入一個事務,執行完目標方法之後根據執行的情況提交或者回滾。

宣告式事務屬於無侵入式,不會影響業務邏輯的實現,只需要在配置檔案中做相關的事務規則宣告或者通過註解的方式,便可以將事務規則應用到業務邏輯中。而程式設計式事務每次實現都要單獨實現,但業務量大功能複雜時,使用程式設計式事務無疑是痛苦的。

顯然宣告式事務管理比程式設計式事務管理更可取,儘管它不如程式設計式事務管理靈活,這正是 Spring 倡導的非侵入式的程式設計方式。

順便了解下註解 @Transactional 的作用範圍:

  1. 方法 :推薦將註解使用於方法上,不過需要注意的是:該註解只能應用到 public 方法上,否則不生效。
  2. 類 :如果這個註解使用在類上的話,表明該註解對該類中所有的 public 方法都生效。
  3. 介面 :不推薦在介面上使用。

Spring 事務管理的五大屬性

1、事務的隔離級別

當有多個事務同時執行時,就可能出現下面這些問題:

  • 髒讀:一個事務讀取了另一個事務未提交的資料。
  • 不可重複讀:一個事務先後讀取相同的資料,發現兩次讀取的資料內容不一致。
  • 幻讀:一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其它事務插入了滿足其查詢條件的新資料

而事務的隔離級別就定義了併發事務活動影響的程度。

  • ISOLATION_DEFAULT:使用後端資料庫預設的隔離級別(MySQL 預設採用的REPEATABLE_READ 隔離級別 Oracle 預設採用的 READ_COMMITTED 隔離級別)

  • ISOLATION_READ_UNCOMMITTED:允許讀取尚未提交的更改,這是最低的隔離級別,很少會使用。可能導致髒讀、幻讀或不可重複讀

  • ISOLATION_READ_COMMITTED:允許從已經提交的併發事務讀取。可防止髒讀,但幻讀和不可重複讀仍可能會發生

  • ISOLATION_REPEATABLE_READ:對相同欄位的多次讀取的結果是一致的,除非資料被當前事務本身改變。可防止髒讀和不可重複讀,但幻讀仍可能發生。

  • ISOLATION_SERIALIZABLE: 最高的隔離級別,完全服從 ACID 的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止髒讀、不可重複讀以及幻讀。但是這在所有隔離級別中也是效能最差的,因為它通常是通過完全鎖定當前事務所涉及的資料表來完成的。通常情況下也不會用到該級別。

2、事務的傳播機制

事務的傳播機制是為了解決事務巢狀的場景下,如何處理事務的問題。

  • PROPAGATION_REQUIRED(預設):

    • 如果該方法執行在沒有事務的方法中,就建立一個新的事務。
    • 如果執行在已經存在事務的方法中,則加入到這個事務中,合併成一個事務。
  • PROPAGATION_SUPPORTS:

    • 如果該方法執行在沒有事務的方法中,就以非事務方式執行。
    • 如果執行在已經存在事務的方法中,則加入到這個事務中,合併成一個事務。
  • PROPAGATION_MANDATORY:

    • 如果該方法執行在沒有事務的方法中,就丟擲異常。
    • 如果執行在已經存在事務的方法中,則加入到這個事務中,合併成一個事務。
  • PROPAGATION_REQUIRES_NEW:

    • 無論該方法是否執行在事務的方法中,都建立一個新的事務。
    • 不過如果執行在存在事務的方法中,就將方法中的事務暫時掛起。
    • 新的事務會獨立提交與回滾,不受呼叫它的父方法的事務影響。
  • PROPAGATION_NOT_SUPPORTED:

    • 無論該方法是否執行在事務的方法中,都以非事務方式執行。
    • 不過如果執行在存在事務的方法中,就將該事務暫時掛起。
  • PROPAGATION_NEVER:

    • 如果該方法執行在沒有事務的方法中,就也以非事務方式執行。
    • 不過如果執行在存在事務的方法中,就丟擲異常。
  • PROPAGATION_NESTED:

    • 如果該方法執行在沒有事務的方法中,就建立一個新的事務。
    • 如果執行在已經存在事務的方法中,則在當前事務中巢狀建立子事務執行。
    • 被巢狀的事務可以獨立於封裝事務進行提交或回滾。
    • 如果外部事務提交巢狀事務也會被提交,如果外部事務回滾巢狀事務也會進行回滾。

3、只讀

如果一個事務只包含了對資料庫查詢操作,那麼可以利用事務的只讀特性,將事務型別指定為 readonly 。只讀事務下,資料庫會進行一些優化。

有同學可能會問了,為什麼查詢操作還要加事務?

例如在做統計查詢或者報表查詢這樣的場景下,可能會涉及多個查詢,多條查詢 SQL 必須保證整體的讀一致性,否則,在前一條 SQL 查詢之後,後一條 SQL 查詢之前,資料被其他使用者改變,那麼這次整體的統計查詢將會出現讀資料不一致的狀態,此時,就應該啟用事務支援。

4、事務超時

事務超時指的是一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,就會自動回滾事務,避免長時間執行的事務會不必要地佔用資料庫資源。

5、回滾規則

預設情況下,事務只在出現執行時異常(runtime exception)時回滾,Error 也會導致事務回滾,但是在出現受檢查異常(checked exception)時不回滾。

不過,可以宣告在出現特定受檢查異常時像執行時異常一樣回滾。同樣,也可以宣告一個事務在出現特定的異常時不回滾,即使特定的異常是執行時異常。

總結

以上就是靚仔對 Spring 事務的理解,如有不足,也歡迎朋友們指正。

後面將會為大家介紹事務失效的幾大場景,以及解決方案,敬請期待!

往期推薦

最詳細的圖文解析Java各種鎖(終極篇)

SpringBoot+Redis 實現訊息訂閱釋出

什麼?你還不會在GitHub上搜索資源?還不點進來看看?

一定要收藏的5個優秀的SpringCloud開源專案

一定要收藏的5個後臺管理系統的前端框架

▼更多精彩推薦,請關注靚仔▼