本地事務簡介
1 事務基本性質
數據庫事務的幾個特性:原子性(Automicity)、一致性(Consistency)、隔離性或獨立性(islation)和持久性(Durability),簡稱ACID。
-
原子性:一系列的操作,其整體不可拆分,要么同時成功,要么同時失敗。
-
一致性:數據在事務的前后,業務整體一致。
- 轉賬 。A:1000; B:1000 ;轉200
事務成功。 A:800, B:1200
- 轉賬 。A:1000; B:1000 ;轉200
-
隔離性:事務之間互相隔離。
-
持久性:一旦事務成功,數據一定會記錄在數據庫。
2 事務的隔離級別
隔離級別 | 特點 |
---|---|
Read Uncommitted | 允許讀取未提交的數據 |
Read committed | 只能讀取已提交的數據 |
Repeatable Read | 同一事務中多次讀取同一數據結果一致 |
Serializable | 事務完全串行化,按順序執行 |
2.1 Read Uncommitted
場景描述
假設有一個銀行賬戶表 accounts
,記錄用戶的賬戶余額。現在有兩個并發事務:
- 事務 A:從賬戶中扣除 100 元。
- 事務 B:查詢賬戶余額。
數據庫初始狀態
假設賬戶初始余額為 1000 元。
事務操作步驟
- 事務 A 開始:
- 查詢賬戶余額:
SELECT balance FROM accounts WHERE account_id = 1;
(結果為 1000 元) - 扣除 100 元:
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
(此時賬戶余額變為 900 元,但事務 A 還未提交)
- 查詢賬戶余額:
- 事務 B 開始:
- 查詢賬戶余額:
SELECT balance FROM accounts WHERE account_id = 1;
(結果為 900 元,因為事務 A 的未提交數據被讀取到了)
- 查詢賬戶余額:
- 事務 A 回滾:
- 由于某些原因,事務 A 回滾,撤銷了之前的更新操作。此時賬戶余額恢復為 1000 元。
- 事務 B 結束:
- 事務 B 查詢到的余額是 900 元,但實際上賬戶的真實余額是 1000 元。
問題分析
- 臟讀:事務 B 讀取到了事務 A 未提交的數據(900 元),而事務 A 最終回滾,導致事務 B 查詢到的數據是不正確的。這種現象稱為臟讀。
Read Uncommitted
的特點
- 在
Read Uncommitted
隔離級別下,事務可以讀取到其他事務尚未提交的數據。 - 這種隔離級別允許并發性能最高,但數據一致性最差,容易出現臟讀問題。
2.2 Read Committed
場景描述
假設有一個庫存表 inventory
,記錄商品的數量。現在有兩個并發事務:
- 事務 A:更新商品的數量。
- 事務 B:查詢商品的數量。
數據庫初始狀態
假設商品的初始數量為 100。
事務操作步驟
- 事務 A 開始:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 100) - 更新商品數量:
UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 1;
(此時商品數量變為 90,但事務 A 還未提交)
- 查詢商品數量:
- 事務 B 開始:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 100,因為事務 A 的更新尚未提交)
- 查詢商品數量:
- 事務 A 提交:
- 提交事務 A,更新操作生效,商品數量變為 90。
- 事務 B 再次查詢:
- 再次查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 90,因為事務 A 已提交)
- 再次查詢商品數量:
問題分析
- 避免臟讀:事務 B 在事務 A 提交之前查詢到的商品數量是 100,而不是事務 A 未提交的 90。這避免了臟讀問題。
- 不可重復讀:事務 B 在事務 A 提交后再次查詢,結果從 100 變為 90。這種現象稱為不可重復讀,因為同一個事務在不同時間讀取到的數據不一致。
- 幻讀:如果事務 B 在事務 A 提交之前查詢商品數量,然后事務 A 插入了一條新的商品記錄,事務 B 再次查詢時可能會發現多了一條記錄。這種現象稱為幻讀。
Read Committed
的特點
- 避免臟讀:事務只能讀取到其他事務已經提交的數據。
- 可能出現不可重復讀和幻讀:由于事務 B 在事務 A 提交前后讀取到的數據不一致,可能會出現不可重復讀和幻讀問題。
- 并發性能較好:相比更高隔離級別(如
Repeatable Read
和Serializable
),Read Committed
的并發性能更好,因為它允許更多的并發操作。
2.3 Repeatable Read
場景描述
假設有一個商品庫存表 inventory
,記錄商品的數量。現在有兩個并發事務:
- 事務 A:查詢并更新商品的數量。
- 事務 B:查詢商品的數量。
數據庫初始狀態
假設商品的初始數量為 100。
事務操作步驟
- 事務 A 開始:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 100) - 更新商品數量:
UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 1;
(此時商品數量變為 90,但事務 A 還未提交)
- 查詢商品數量:
- 事務 B 開始:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 100,因為事務 A 的更新尚未提交)
- 查詢商品數量:
- 事務 A 提交:
- 提交事務 A,更新操作生效,商品數量變為 90。
- 事務 B 再次查詢:
- 再次查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果仍為 100,因為事務 B 在同一個事務中,讀取到的結果是一致的)
- 再次查詢商品數量:
問題分析
- 避免臟讀:事務 B 在事務 A 提交之前查詢到的商品數量是 100,而不是事務 A 未提交的 90。這避免了臟讀問題。
- 避免不可重復讀:事務 B 在同一個事務中多次查詢商品數量,結果始終為 100,即使事務 A 已提交,事務 B 仍然讀取到的是事務開始時的一致數據。這避免了不可重復讀問題。
- 可能出現幻讀:雖然
Repeatable Read
避免了不可重復讀,但在某些數據庫實現中,如果事務 A 插入或刪除了記錄,事務 B 可能會看到不同的結果集。這種現象稱為幻讀。
Repeatable Read
的特點
- 避免臟讀和不可重復讀:事務在同一個事務中多次讀取同一數據的結果是一致的。
- 可能出現幻讀:在某些數據庫實現中,如果事務 A 插入或刪除了記錄,事務 B 可能會看到不同的結果集。
- 并發性能較好:相比
Serializable
,Repeatable Read
的并發性能更好,因為它允許更多的并發操作。
2.4 Serializable
場景描述
假設有一個商品庫存表 inventory
,記錄商品的數量。現在有兩個并發事務:
- 事務 A:查詢并更新商品的數量。
- 事務 B:查詢商品的數量。
數據庫初始狀態
假設商品的初始數量為 100。
事務操作步驟
- 事務 A 開始:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 100) - 更新商品數量:
UPDATE inventory SET quantity = quantity - 10 WHERE product_id = 1;
(此時商品數量變為 90,但事務 A 還未提交)
- 查詢商品數量:
- 事務 B 開始:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(事務 B 會被阻塞,直到事務 A 提交或回滾)
- 查詢商品數量:
- 事務 A 提交:
- 提交事務 A,更新操作生效,商品數量變為 90。
- 事務 B 繼續執行:
- 查詢商品數量:
SELECT quantity FROM inventory WHERE product_id = 1;
(結果為 90,因為事務 A 已提交)
- 查詢商品數量:
問題分析
- 避免臟讀:事務 B 在事務 A 提交之前無法讀取數據,因此不會讀取到事務 A 未提交的數據。
- 避免不可重復讀:事務 B 在同一個事務中多次查詢商品數量,結果始終為 90,即使事務 A 已提交,事務 B 仍然讀取到的是事務開始時的一致數據。
- 避免幻讀:事務 B 在事務 A 提交之前無法讀取數據,因此不會看到事務 A 插入或刪除的記錄。
Serializable
的特點
- 完全避免并發問題:
Serializable
確保所有事務按順序執行,完全避免了臟讀、不可重復讀和幻讀。 - 并發性能最低:由于事務必須按順序執行,不能并行,因此并發性能最低。
- 適用場景:適用于對數據一致性要求極高的場景,如金融交易系統、賬務系統等。
3 事務的傳播行為
在 Spring 中,事務傳播行為(Transaction Propagation Behavior)定義了在一個事務中調用另一個事務方法時,事務如何被傳播和管理。Spring 提供了多種事務傳播行為,每種行為都對應不同的事務管理策略。以下是常見的事務傳播行為及其解釋:
傳播行為 | 當前有事務時 | 當前無事務時 |
---|---|---|
PROPAGATION_REQUIRED | 加入當前事務 | 創建新事務 |
PROPAGATION_SUPPORTS | 加入當前事務 | 非事務執行 |
PROPAGATION_MANDATORY | 加入當前事務 | 拋出異常 |
PROPAGATION_REQUIRES_NEW | 創建新事務,掛起當前事務 | 創建新事務 |
PROPAGATION_NOT_SUPPORTED | 掛起當前事務,非事務執行 | 非事務執行 |
PROPAGATION_NEVER | 拋出異常 | 非事務執行 |
PROPAGATION_NESTED | 創建嵌套事務 | 創建新事務 |
實例代碼:
#在注解上指定事務的傳播行為
@Transational(Propagation=Propagation.REQUIRES_NEWS)
#方法簽名
public void transaction(){...}
4 代理對象
在同一個類中,編寫兩個方法,內部調用的時候,會導致事務設置失效。原因是沒有用到代理對象。
// TODO ps:貌似新版spring已經優化了,可以內部調用了。
解決方法:通過代理對象調用方法
①引入依賴
<dependency><groupId>org.springframe.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
②在啟動類上加上注解@EnableAspectJAutoProxy(exposeProxy=true)
@EnableAspectJAutoProxy(exposeProxy=true)
@SpringBootApplication
public class Application {...}
③在類中使用代理對象如下:
OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
// 在OrderServiceImpl類內部,通過代理對象調用自身的method1方法和method2方法
orderService.method1();
orderService.method2();