天天看點

談談mysql中事務的實作原理

緣起

你是否還記得,我們在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;)