Mysql事務結合spring管理


spring事務相關問題記錄

遇到情況:

在本地單體應用調試代碼時,發現在一個加了@transaction注解的方法里進行先更新后查詢的操作,查詢的結果是可以看到更新的內容的。而在微服務環境中同樣的代碼卻在后查詢時查不到前面更新的內容。

偽代碼如下:

@Transactional
	public void call() {
		bidPlanMapper.updateByPrimaryKey(plan);
		List<BidPlan> all = systemService.findBidPlan();
		System.out.println(all);//單體應用時可觀察到更改的結果
	}

猜測本地可以而微服務上不可以,估計是因為微服務在執行call()方法時,update操作是由biz服務調用的,而findBidPlan()操作是實際是調用system服務進行的(該方法也有@Transactional),故產生的是兩個事務。

處理辦法:
  • update操作后立刻提交事務,則另外一個事務可以查詢到處理的結果。
@Autowired
private DataSourceTransactionManager transactionManager;

public void call() {
    //開啟新事務
	DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
	transDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
	TransactionStatus transStatus = transactionManager.getTransaction(transDefinition);
    try {
        bidPlanMapper.updateByPrimaryKey(plan);//先提交更新的操作
        transactionManager.commit(transStatus);
    } catch (Exception e) {
        transactionManager.rollback(transStatus);
    }
}

擴展閱讀:

隔離級別:

隔離級別定義一個事務可能受其他並發事務活動活動影響的程度。另一種考慮一個事務的隔離級別的方式,是把它想象為那個事務對於事物處理數據的自私程度。

在一個典型的應用程序中,多個事務同時運行,經常會為了完成他們的工作而操作同一個數據。並發雖然是必需的,但是會導致以下問題

  • 臟讀(Dirty read)-- 臟讀發生在一個事務讀取了被另一個事務改寫但尚未提交的數據時。如果這些改變在稍后被回滾了,那么第一個事務讀取的數據就會是無效的。

  • 不可重復讀(Nonrepeatable read)-- 不可重復讀發生在一個事務執行相同的查詢兩次或兩次以上,但每次查詢結果都不相同時。這通常是由於另一個並發事務在兩次查詢之間更新了數據。(即不可重復讀到同值)

  • 幻影讀(Phantom reads)-- 幻影讀和不可重復讀相似。當一個事務(T1)讀取幾行記錄后,另一個並發事務(T2)插入了一些記錄時,幻影讀就發生了。在后來的查詢中,第一個事務(T1)就會發現一些原來沒有的額外記錄。

隔離級別 含義
ISOLATION_DEFAULT 使用后端數據庫默認的隔離級別。
ISOLATION_READ_UNCOMMITTED 允許讀取尚未提交的更改。可能導致臟讀、幻影讀或不可重復讀。
ISOLATION_READ_COMMITTED 允許從已經提交的並發事務讀取。可防止臟讀,但幻影讀和不可重復讀仍可能會發生。
ISOLATION_REPEATABLE_READ(MySQL默認隔離級別) 對相同字段的多次讀取的結果是一致的,除非數據被當前事務本身改變。可防止臟讀和不可重復讀,但幻影讀仍可能發生。
ISOLATION_SERIALIZABLE 完全服從ACID的隔離級別,確保不發生臟讀、不可重復讀和幻影讀。這在所有隔離級別中也是最慢的,因為它通常是通過完全鎖定當前事務所涉及的數據表來完成的。

1.查看當前會話隔離級別

select @@tx_isolation;

2.查看系統當前隔離級別

select @@gobal.tx_isolation;
模擬read committed隔離級別下出現不可重復讀的狀況:

開啟一個事務A進行三次查詢。事務B在事務A第一和第二次查詢中進行數據更新,事務B在事務A第二次和第三次查詢中進行事務提交。

  • 事務A
mysql> SET SESSION TRANSACTION ISOLATION LEVEL read committed; #設置當前會話事務的隔離級別為read committed

mysql> select @@tx_isolation; #查詢當前會話事務的隔離級別
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

mysql> start transaction; #開啟事務A
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t_user; #事務A第一次查詢 (1)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 張三 | 123      | 000   |
+----+------+----------+-------+

###################### 事務B進行更新操作 ##############################

mysql> select * from t_user; #事務A第二次查詢,沒有看到事務B沒有提交的更新操作。說明已經防止了臟讀問題。(3)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 張三 | 123      | 000   |
+----+------+----------+-------+

###################### 事務B進行事務提交 ##############################

mysql> select * from t_user; #觀察到事務B提交的結果,說明無法在同個事務中多次查詢值都是一樣的,即無法避免不可重復讀問題。(5)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 張三 | 123      | 123   |
+----+------+----------+-------+

  • 事務B
mysql> start transaction; #開啟事務B 

mysql> update t_user set `phone` = '123' where id = 1; #進行更新操作(2)

mysql> commit; #提交事務B (4)

理解REPEATABLE READ 隔離級別下什么是可重復讀:

開啟兩個事務。

  • 事務A
mysql> begin;

mysql> select * from t_user; #(1)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 張三 | 123      | 188   |
+----+------+----------+-------+

###################### 事務B進行事務提交 ##############################


mysql> select * from t_user; #可觀察到兩次查詢結果都一樣,不會因為事務B的更新提交而有影響(3) 
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 張三 | 123      | 188   |
+----+------+----------+-------+

  • 事務B
mysql> begin;

mysql> update `t_user` set `phone` = '8888'; #(2)

mysql> commit; #(2)

雖然該隔離級別下是,無論事務B進行更新、新增、刪除,在事務A中仍然可以重復讀到相同值的。

但是如果事務A進行更新操作,則可以對事務B新增的數據進行直接更新。

可參考如下例子:

REPEATABLE READ 隔離級別下,MySQL在session1執行UPDATE語句的時候對於session2的INSERT語句是可以看到的,也就是說發生了幻讀。
相關閱讀:MySQL在REPEATABLE READ 隔離級別下的工作方式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM