請簡單介紹Spring支持的常用數據庫事務傳播屬性和事務隔離級別?
事務的屬性
-
propagation
:用來設置事務的傳播行爲事務的傳播行爲:一個方法運行在了一個開啓了事務的方法中,當前方法是使用原來的事務還是開啓一個新的事務
Propagation.REQUIRED
:默認值,使用原來的事務Propagation.REQUIRES_NEW
:將原來的事務掛起,開啓一個新的事務
-
isolation
:用來設置事務的隔離級別Isolation.REPEATEBLE_READ
:可重複讀,MySQL默認的隔離級別Isolation.READ_COMMITTED
:讀已提交,Oracle默認的隔離級別,開發時通常使用的隔離級別
事務的傳播簡介
當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼承在現有事務中運行,也可能開啓一個新事務,並在自己的事務中運行。
事務的傳播行爲可以由傳播屬性指定。Spring定義了7種類型的傳播行爲。
傳播屬性 | 描述 |
---|---|
REQUIRED (默認) | 如果有事務在運行,當前的方法就在這個事務內運行,否則,就啓動一個新的事務,並在自己的事務內運行 |
REQUIRES_NEW | 當前的方法必須啓動新的事務,並在它自己的事務內運行,如果有事務正在運行,應該將它掛起 |
SUPPORTS | 如果有事務正在運行,當前的方法就在這個事務內運行。否則它可以不運行在事務中 |
NOT_SUPPORTED | 當前的方法不應該運行在事務中。如果有運行的事務,將它掛起 |
MANDATORY | 當前的方法必須運行在事務內部。如果沒有正在運行的事務,就拋出異常 |
NEVEER | 當前的方法不應該運行在事務中。如果有運行的事務,就拋出異常 |
NESTED | 如果有事務在運行,當前的方法就應該在這個事務的嵌套事務內運行。否則,就啓動一個新的事務,並在它自己的事務內運行 |
事務的傳播屬性可以在@Transactional
註解的propagation
屬性中定義。
(常用的是REQUIRED (默認)、REQUIRES_NEW這兩個)
示例
用戶表
id用戶id | username用戶名 | balance餘額 |
---|---|---|
1 | zhangsan | 100.00 |
商品表
isbn書的編號 | name書名 | price單價 |
---|---|---|
1001 | 三國演義 | 60.00 |
1002 | 西遊記 | 50.00 |
庫存表
isbn書的編號 | stock庫存 |
---|---|
1001 | 100 |
1002 | 100 |
分析
以上代碼中,checkout跟purchase方法都使用了事務,事務的傳播默認值是REQUIRED (默認,如果有事務在運行,當前的方法就在這個事務內運行),所以是使用checkout方法的事務。它購買了兩本書1001和1002,購買1001單價60的成功,但餘額變成了100-60=40,不足以購買1002的書,所以失敗,回滾。結果是,一本也沒有買到,數據庫數據不變。
分析
如果在purchase中添加了屬性Propagation.REQUIRES_NEW
,就會開啓新的事務(當前的方法必須啓動新的事務,並在它自己的事務內運行,如果有事務正在運行,應該將它掛起),則將checkout事務掛起了,purchase使用自己的事務,這樣,1001書本會成功購買,庫存變爲99,用戶餘額變爲40,會買成功一本。1002單價是50,錢不夠,所以買不了。
事務的隔離級別
1. 數據庫事務併發問題
假設現在有兩個事務:Transaction01和Transaction02併發執行
- 髒讀
- Transaction01將某條記錄的AGE值從20修改爲30
- Transaction02讀取了Transaction01更新後的值:30
- Transaction01回滾,AGE值恢復到了20
- Transaction02讀到的30就是一個無效的值
- 不可重複讀
- Transaction01讀取了AGE值爲20
- Transaction02將AGE值修改爲30
- Transaction01再次讀取AGE值爲30,和第一次讀取的不一致
- 幻讀
- Transaction01讀取了stu表中的一部分數據
- Transaction02向stu表中插入了新的行
- Transaction01讀取stu表時,多出了一些行
2. 事務的隔離級別
數據庫系統必須具有隔離併發運行各個事務的能力,使他們不會相互影響,避免各種併發問題。一個事務與其他事務隔離的程度稱爲隔離級別。SQL標準中規定了好多事務隔離級別,不同隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好,但併發性越弱
-
讀未提交:READ UNCOMMITTED
允許Transaction01讀取Transaction02未提交的修改
(上面三種情況都不可避免)
-
讀已提交:READ COMMITTED(常用的,其他事務已經提交的數據都認爲是真的)
要求Transaction01只能讀取Transaction02已提交的修改
(可避免髒讀)
-
可重複讀:REPEATABLE READ
確保Transaction01可以多次從一個字段中讀取到相同的值,即Transaction01執行期間禁止其他事務對這個字段進行更新。
(可避免髒讀,不可重複讀)
-
串行化:SERIALIZABLE
確保Transaction01可以從一個表中讀取到相同的行,在Transaction01執行期間,禁止其他事務對這個表進行增刪改操作。可以避免任何併發問題,但性能十分低下。
各個隔離級別解決併發問題的能力見下表
髒讀 | 不可重複讀 | 幻讀 | |
---|---|---|---|
READ UNCOMMITTED | 有 | 有 | 有 |
READ COMMITTED | 無 | 有 | 有 |
REPEATABLE READ | 無 | 無 | 有 |
SERIALIZABLE | 無 | 無 | 無 |
各種數據庫產品對事務隔離級別的支持程度
Oracle | MySQL | |
---|---|---|
READ UNCOMMITTED | x | √ |
READ COMMITTED | √(默認) | √ |
REPEATABLE READ | x | √(默認) |
SERIALIZABLE | √ | √ |
示例:
可在@Transactional
中添加isolation
屬性