事務原理-- Spring 事務管理詳解

一、事務的基本原理

一個數據庫事務是一個被視爲單一的工作單元的操作序列,這些操作應該要麼完整地執行,要麼完全不執行。事務的概念可以描述爲具有以下四個關鍵屬性說成是ACID:

  1. 原子性:事務應該當作一個單獨單元的操作,這意味着整個序列操作要麼是成功的,要麼是失敗的。
  2. 一致性:這表示數據庫的引用完整性的一致性,表中唯一的主鍵等
  3. 隔離性:可能同時處理很多有相同的數據集的事務,每個事務應該與其他事務隔離,以防止數據損壞
  4. 持久性:一個事務一旦完成全部操作後,這個事務的結果必須是永久性的,不能因系統故障而從數據庫中刪除。

Spring事務的本質其實是數據庫對事務的支持,對於純JDBC操作數據庫,想要用事務,可以按照以下步驟進行:

  • 獲取連接 Connection conn = DriverManager.getConnection();
  • 開啓事物 conn.setAutoCommit(true/false);
  • 執行sql
  • 提交事務/回滾事務 conn.commit()/conn.rollback();
  • 關閉連接 conn.close();

使用Spring的事務管理功能後,我們可以不再寫步驟 2 和 4 的代碼,而是由Spirng 自動完成。

  • 配置文件開啓註解驅動,在相關的類和方法上通過註解@Transaction標識
  • Spring在啓動的時候去解析生成相關的bean,根據@Transaction的相關參數進行配置注入,處理事務

二、spring 事務的傳播屬性

所謂spring事務的傳播屬性,就是定義在存在多個事務同時存在的時候,spring應該如何處理這些事務的行爲

  1. PROPAGATION_REQUIRED支持當前事務,加入當前正要執行的事務不在另一個事務裏,那麼就另起一個新的事務。比如說,ServiceB.methodB的事務級別定位爲PROPAGATION_REQUIRED,那麼由於執行ServiceA.methodA時,已經起了事務,這時調用ServiceB.methodB,由於運行在ServiceA.methodA事務內部,就不再起新的事務。假如ServiceA.methodA運行的時候發現自己沒有在事務中,ServiceB.methodB運行時會爲自己分配一個事務,這樣,ServiceB.methodB和ServiceA.methodA內的任何地方出現異常,事務都會被回滾,即時ServiceB.methodB的事務已經被提交,但是ServiceA.methodA回滾時ServiceB.methodB也要回滾
  2. PROPAGATION_SUPPORTS 如果當前在事務中,即以事務的形式運行,如果當前不在一個事務中,那麼就以非事務的形式運行
  3. PROPAGATION_MANDATORY  必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常
  4. PROPAGATION_REQUIRES_NEW 這個就比較繞口了。 比如我們設計ServiceA.methodA的事務級別爲PROPAGATION_REQUIRED,ServiceB.methodB的事務級別爲PROPAGATION_REQUIRES_NEW,那麼當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以後,他才繼續執行。他與PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度了。因爲ServiceB.methodB是新起一個事務,那麼就是存在兩個不同的事務。如果ServiceB.methodB已經提交,那麼ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能提交。

  5. PROPAGATION_NOT_SUPPORTED 當前不支持事務。比如ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,那麼當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。

  6. PROPAGATION_NEVER 不能在事務中運行。假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,那麼ServiceB.methodB就要拋出異常了。

  7. PROPAGATION_NESTED 理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立,而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後回滾,他也要回滾的。而Nested事務的好處是他有一個savepoint。

 

三、事務的四種隔離級別

Read uncommitted

讀未提交,顧名思義,就是一個事務可以讀取另一個未提交事務的數據

事例:老闆要給程序員發工資,程序員的工資是3.6萬/月。但是發工資時老闆不小心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,但是事務還沒有提交,就在這時,程序員去查看自己這個月的工資,發現比往常多了3千元,以爲漲工資了非常高興。但是老闆及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。

分析:實際程序員這個月的工資還是3.6萬,但是程序員看到的是3.9萬。他看到的是老闆還沒提交事務時的數據。這就是髒讀。那怎麼解決髒讀呢?Read committed!讀提交,能解決髒讀問題。

 

Read committed

 

讀提交,顧名思義,就是一個事務要等另一個事務提交後才能讀取數據。

事例:程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程序員事務開啓),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢全部轉出充當家用,並提交。當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程序員就會很鬱悶,明明卡里是有錢的…

分析:這就是讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交後才能讀取數據,可以解決髒讀問題。但在這個事例中,出現了一個事務範圍內兩個相同的查詢卻返回了不同數據,這就是不可重複讀。

那怎麼解決可能的不可重複讀問題?Repeatable read !

Repeatable read

 

重複讀,就是在開始讀取數據(事務開啓)時,不再允許修改操作

事例:程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(事務開啓,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡里有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。

分析:重複讀可以解決不可重複讀問題。寫到這裏,應該明白的一點就是,不可重複讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因爲幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。

什麼時候會出現幻讀?

事例:程序員某一天去消費,花了2千元,然後他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啓),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。

 

那怎麼解決幻讀問題?Serializable!

 

Serializable 序列化

 

Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免髒讀、不可重複讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。

值得一提的是:大多數數據庫默認的事務隔離級別是Read committed,比如Sql Server , OracleMySQL的默認隔離級別是Repeatable read。

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