MySQL學習之事務隔離

thinkabel發表於2021-02-03

圖片

學習了基礎架構, 日誌系統。今天看一下,我們平時在日常開發中用的最多的事務。

最經典的例子,就是你要給你的朋友王麻子轉100塊錢,而此時你的銀行卡中只有100塊錢。轉賬的過程具體到程式裡會有一系列的操作,比如查詢餘額、做加減法、更新餘額等操作。

這些操作必須保證是一體的,不然等程式查完後,你“趁著程式還沒反應過來”進行扣減,你再給另一個朋友轉去100元,這豈不是亂套了嗎?這個時候就要用到 事務 了。

一、概念

事務是應用程式中一系列嚴密的操作,所有操作必須成功完成,否則在每個操作中所作的所有更改都會被撤消。也就是事務具有原子性,一個事務中的一系列的操作要麼全部成功,要麼一個都不做。

事務的結束有兩種,當事務中的所以步驟全部成功執行時,事務提交。如果其中一個步驟失敗,將發生回滾操作,撤消撤消之前到事務開始時的所以操作。

在MySQL中,事務是在引擎層進行實現的,MySQL是一個支援多引擎的系統,但是並不是所有的引擎都是支援事務的。MySQL 原生的MyISAM就是不支援事務,這也是MySQL5.5之後,MyISAM被InnoDB取代的重要原因之一。

二、隔離級別

提到事務,我們第一反應應該就是事務的四大特性,原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、永續性(Durability),簡稱 ACID。其他的通過字面意思都還是比較好理解的,我們看下這個 “隔離性”是怎麼玩的。

我們知道,在資料庫上進行多個事務同時執行的時候,就可能會出現髒讀(dirty read)、不可重複讀(non-repeatable read)、幻讀(phantom read)的問題,為了解決這些問題,引出了隔離級別這個概念。

Tips:
髒讀:讀到其他事務未提交的資料;

不可重複讀:前後讀取的記錄內容不一致(資料);

幻讀:前後讀取的記錄資料不一致(條數);

SQL標準的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(red committed)、可重複讀(repeatable read)、序列化(serialzable)。這些分別都是什麼意思呢?別急,解釋來了:

  • 讀未提交:一個事務還沒有提交時,他做的變更資料就能被其他的事務看到。

  • 讀 提 交 :一個事務提交之後,他做得變更資料才能被其他事務看到。

  • 可重複讀:一個事務執行過程中看到的資料,總是跟這個事務啟動時看到的資料是一致的。(在可重複讀隔離級別下,未提交的變更對其他事務是不可見的),換句話就是 一個事務啟動時,讀到的是什麼資料,在執行過程中,就是什麼資料,不會發生改變,無論別的事務是否對其進行改動。

  • 串 行 化 :對同一行資料記錄,寫時會加寫鎖,讀時會加讀鎖。當出現讀寫鎖衝突時,後訪問的事務必須要等待前一個事務完成,才能進行。換句話也就是說,我執行的時候,別的事務是不能查詢到或者修改這一行資料的。

舉個例子:

mysql> create table D(a int) engine=InnoDB;
insert into D(a) values(1);
事務A 事務B
啟動事務查詢得到值1 啟動事務
查詢得到值1
將1改為2
查詢得到值V1
提交事務B
查詢得到值V2
提交事務A
查詢得到值V3

(事務演示 列表)

看下在不同的隔離級別下,事務A都會有什麼結果。

  • 讀未提交:V1 的值 是 2 。事務B雖然還沒有提交,但是結果A已經看到了,所以V2,V3結果都是2 。

  • 讀 提 交 :V2 的值 是 1 。事務B提交,但是A是看不到的,所以V2 是 1。這時事務A提交,V3的值 是 2。

  • 可重複讀 :V1 ,V2 的值 是 1。V3 的是 2。V1,V2之所以是1,就是因為遵循事務執行期間,看到的資料結果是一致的。

  • 串 行 化 :事務A啟動時,進行查詢,這時會加讀鎖,事務B在進行讀取,要等到事務A釋放,才能進行讀取。等到事務A提交事務,事務B進行執行,這時V1,V2的值是1,V3的值為2。

三、什麼時候建立檢視

為什麼有的是能看到有的卻看不到呢?因為在實現上,資料庫會建立一個檢視,訪問的時候以檢視的邏輯結果為準,進行訪問。在每個隔離級別下,檢視建立的時間也是不同的,資料庫的行為是有所不同的。

  • 可重複讀:檢視是在事務啟動時進行建立的,整個事務存在期間都用這個檢視。

  • 讀 提 交 :檢視是在每個SQL語句開始執行時進行建立的。

  • 讀未提交:直接返回記錄上的最新值,沒有檢視概念。

  • 串 行 化 :採用鎖的機制來避免並行訪問。

也就是說。

讀提交的情況下,會在每一個語句前建立一個檢視,所以在讀提交情況下,一個事務是可以看到其他事務提交的內容,因為它在每次查詢之前都會重新用最新的資料建立一個檢視。

*可重複讀的級別下,檢視是在開始事務的時候就建立好了,這個檢視會一直使用,一直到這個事務結束。
*

四、事務隔離的實現

在MySQL中,每條記錄在更新的時候都會記錄一條變更記錄(redo log),也同時會記錄一條變更相反的回滾操作記錄(undo log)。

假設一個值從 2 被按順序改成了3、4、5,在回滾日誌裡面就會有類似下面的記錄

圖片

( 回滾日誌 演示)

  • 當前值是 4,不同時刻啟動的事務會有不同的 read-view

  • 在檢視 A、B、C 裡面,這一個記錄的值分別是 1、2、4,同一條記錄在系統中可以存在多個版本,就是資料庫的多版本併發控制(MVCC)

  • 當沒有事務再需要用到這些回滾日誌時,回滾日誌會被刪除

同時你會發現,即使現在有另外一個事務正在將 4 改成 5,這個事務跟 read-view A、B、C 對應的事務是不會衝突的。

我想你一定有一個疑問,回滾日誌總不能一直保留吧,什麼時候刪除呢?

在不需要的時候才刪除。也就是說,系統會判斷,當沒有事務再需要用到這些回滾日誌時,回滾日誌會被刪除。什麼時候才不需要了呢?就是當系統裡沒有比這個回滾日誌更早的 read-view 的時候。

五、事務的啟動方式

  • 顯式啟動事務語句beginstart transaction,配套的提交語句是 commit,回滾語句是 rollback

  • set autocommit=0:該命令會將這個執行緒的自動提交關掉,如果你只執行一個 select 語句,這個事務就啟動了,而且並不會自動提交

圖片

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章