1. 程式人生 > >Transactional超時時間控制

Transactional超時時間控制

專案使用的是spring+mybatis+mysql,今天,我需要把處理一個業務就是,當用戶出金失敗時,事務能夠回滾,同時減少使用者的等待時間,因為我發現當處理失敗時,使用者需要等上1分鐘以上的時間,這是不合理的。那麼經過一系列的調查發現:spring的事務超時(使用Java註解方式)和mysql InnoDB事務超時是相互關聯的

在一個需要進行事務回滾的方法上加入@Transactional的事務註解,timeout超時時間設定為2秒,也就是說發生事務回滾後,2秒鐘後對使用者響應。

	@Transactional(timeout=2)
	public int updateForzenMoney(MoneyTransfer moneyTransfer, int uid) {
		// 如果是出金請求,則預扣除凍結資金
		if (moneyTransfer.getType().intValue() == 1) {
			moneyTransferService.updateTotalmoneyForPerTransfermoney(uid, moneyTransfer.getAmount());
			// 將請求插入money_transfer表中
			moneyTransferService.addMoneyTransfer(moneyTransfer);
			return 1;
		} else {
			moneyTransferService.addMoneyTransfer(moneyTransfer);
			return 2;
		}
	}
通過mysql控制檯的模擬操作(強迫發生事務回滾),發現updateForzenMoney方法前後執行時間遠遠大於2秒的時間,為什麼呢?

先想到的是不是mybatis的原因,因為mybatis在xml定義update語句時也提供了超時時間設定,見如下說明,

timeout單位是毫秒
這個設定驅動程式等待資料庫返回請求結果,並丟擲異常時間的最大等待值。預設不設定(驅動自行處理)。
OK,我對update語句加上超時2秒處理
<update id="updateTotalmoneyForPerTransfermoneyOut" parameterType="hashmap" timeout="2000">
繼續執行,發現執行時間依然遠遠大於2+2+2,接近50毫秒,這期間又測試了很多種組合方式,然後我看到mysql的配置檔案(my.cnf)中,innodb_lock_wait_timeout=50,而我所使用的表剛好是innodb型別。
mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set
ok,找到了他,那麼到底是不是呢,修改一下
mysql> set
 innodb_lock_wait_timeout = 10;
Query OK, 0 rows affected

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 10    |
+--------------------------+-------+
1 row in set
繼續執行,發現執行時間接近了10秒左右,然後再將10秒設定為20秒,ok,測試出來的超時時間大概是20秒,說明這個思路是正確的,但是為什麼spring的事務超時時間沒有起到作用呢,繼續調查把mysql的時間再設定短一點,然後再次把spring的事務超時設定為5秒,把mybatis的超時去掉
mysql> set innodb_lock_wait_timeout = 1
;
Query OK, 0 rows affected

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 1     |
+--------------------------+-------+
1 row in set
	@Transactional(timeout=5)
	public int updateForzenMoney(MoneyTransfer moneyTransfer, int uid) {
然後再執行,看日誌時間
DEBUG 2014-12-12 16:53:49,784 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: UPDATE money_us
DEBUG 2014-12-12 16:53:49,785 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 
DEBUG 2014-12-12 16:54:00,795
事務回滾的時間差不多剛好是兩個statement的事務執行時間5+5=10秒的時間

結論:spring的事務超時時間和mysql的事務超時時間是相互影響的!我最後確認的方案是,修改mysql的innodb超時時間為20秒,然後去掉Java方法上的超時時間

@Transactional
public int updateForzenMoney(MoneyTransfer moneyTransfer, int uid) {
再次,測試,事務的回滾時間大概是20秒。

總結:功夫不負有心人,只要一步步深入調查,什麼問題終究會得出答案。