緣起
你是否還記得,我們在spring中是如何使用聲明式事務的呢?沒錯,只需要如下一行簡單的代碼
@Transactional(value="transactionManager", rollbackFor = Exception.class)
那麼你有沒有想過,爲什麼只需要加這樣一個註解,就能實現事務的管理呢?其背後的作用原理是怎樣的呢?
spring中加@Transactional註解就能實現事務的原因淺析
要知道爲什麼加這樣一個註解,就能實現事務的管理你需要了解註解的實現的原理和AOP的實現原理。本文不是探討spring的源碼的,所以不會分析具體的實現原理,下面只列出來一個簡單的原理描述。
原理:
在你執行到加了@Transaction這個方法時,你執行的這個方法已經不是你原來的那個方法了,他是spring幫我們生成的一個代理對象的方法,
如果你瞭解過代理模式,那麼就很容易理解。這個代理方法會在我們本來的方法執行前和執行後做一些事,做什麼事呢?就是在執行前開啓
一個事務然後再執行後提交或者回滾事務。
那麼他的開啓事務和提交回滾事務是怎麼做的呢?這就需要了解mysql中事務的工作工作原理。
mysql中事務的工作原理
不知道你有沒有試過通過sql語句開啓一個事務呢?其實是可以的,在mysql執行
START TRANSACTION; //開啓事務
COMMIT; //提交事務
ROLLBACK; //回滾事務
但是默認情況下mysql中的事務是會自動提交的,比如你執行如下語句
update User set name='張三' where id='1';
雖然你沒有進行任何的提交操作,但是別人仍然能查詢到你的張三,這是因爲mysql默認幫我們提交了。
可以通過如下語句關閉mysql的自動提交
set session autocommit=0; //其中0代表關閉,1代表啓用,默認是1
show variables like '%autocommit%'; //查詢目前自動提交是關閉的還是啓用的,OFF代表關閉,ON代表啓用
上訴只是講述了mysql中如何通過sql開啓事務,那麼mysql中事務的實現原理到底是怎樣的呢?
我們要知道,mysql是一個文件數據庫,也就是說最終mysql中的數據是需要落地到磁盤中的某個文件的。
以下內容轉至:https://blog.csdn.net/LCRxxoo/article/details/79912190
undo 日誌文件
undo記錄了數據在事務開始之前的值,當事務執行失敗或者ROLLBACK時可以通過undo記錄的值來恢復數據。例如 AA和BB的初始值分別爲3,5。
A 事務開始
B 記錄AA=3到undo_buf
C 修改AA=1
D 記錄BB=5到undo_buf
E 修改BB=7
F 將undo_buf寫到undo(磁盤)
G 將data_buf寫到datafile(磁盤)
H 事務提交
通過undo可以保證原子性、穩定性和持久性
如果事務在F之前崩潰由於數據還沒寫入磁盤,所以數據不會被破壞。
如果事務在G之前崩潰或者回滾則可以根據undo恢復到初始狀態。
數據在任務提交之前寫到磁盤保證了持久性。
但是單純使用undo保證原子性和持久性需要在事務提交之前將數據寫到磁盤,浪費大量I/O。
redo/undo 日誌文件
引入redo日誌記錄數據修改後的值,可以避免數據在事務提交之前必須寫入到磁盤的需求,減少I/O。
A 事務開始
B 記錄AA=3到undo_buf
C 修改AA=1 記錄redo_buf
D 記錄BB=5到undo_buf
E 修改BB=7 記錄redo_buf
F 將redo_buf寫到redo(磁盤)
G 事務提交
通過undo保證事務的原子性,redo保證持久性。
F之前崩潰由於所有數據都在內存,恢復後重新衝磁盤載入之前的數據,數據沒有被破壞。
FG之間的崩潰可以使用redo來恢復。
G之前的回滾都可以使用undo來完成。
其實可以簡單的認爲mysql通過兩個臨時變量幫我們保存了修改前的值和修改後的值,如果沒有問題,那麼把修改後的值寫入到磁盤,如果有問題就可以通過修改前的值來回滾數據。
再回過頭說說spring的執行前和執行後做了什麼事
基本上可以預料的是,spring在我們的方法執行前像mysql發送了兩個請求
1、關閉自動提交(set session autocommit=0;)
2、開啓一個事務(START TRANSACTION;)
然後在方法的執行之後再根據我們的方法是否異常發送提交或者回滾的請求,同時開啓自動提交
1、提交(COMMIT)或者 回滾(ROLLBACK)
2、開啓自動提交(set session autocommit=1;)