事務隔離:爲什麼你改了我還看不見?

前言

提到事務,你肯定不陌生,做後端開發或多或少都會用到事務。
簡單來說,事務就是要保證一組數據庫操作,要麼全部成功,要麼全部失敗。在 MySQL 中,事務支持是在引擎層實現的。我們知道,MySQL 是一個支持多引擎的系統,但並不是所有的引 擎都支持事務。比如 MySQL 原生的 MyISAM 引擎就不支持事務,這也是 MyISAM 被 InnoDB 取代的重要原因之一。
事務最經典的例子就是前面說的小明把招行的300元轉到農行中。這裏我們再來說一下數據庫執行步驟,因爲下面要用到:

步驟1.檢查招行中的錢是否大於300
步驟2.招行中的錢減少300
步驟3.農行中的錢增加300

今天的文章裏,我將會以 InnoDB 爲例,剖析 MySQL 在事務支持方面的特定實現,並基於原 理給出相應的實踐建議,希望這些案例能加深你對 MySQL 事務原理的理解。

隔離性與隔離級別

雖然我們是講事務的隔離性,但是爲了學習,這裏還是把事務的4大特徵給列一下:

1.原子性:一個事務必須被視爲一個不可分割的最小工作單位。即要麼成功,要麼失敗回滾。
2.一致性:數據庫總是從一個狀態轉到別一個狀態。打個比方,即使步驟2執行了,步驟3因爲系統崩潰沒有執行,二個賬號裏面的錢沒有任何損失,因爲還沒有提交事務,所以事務中所做的修改是不會保存在數據庫中的。
3.隔離性一個事務所做的修改,在最終提交前對其他事務是不可見的。舉個例子步驟2執行了,步驟3沒有執行。這個時候,另外一個賬號去查看賬號裏面的錢是沒有變化:的,也不是說此時賬號裏面的錢並沒有減去300.
4.持久性:一旦事務提交,那麼數據庫中的數據是永久改變的。此時即使系統崩潰了,數據也不會丟失。

提到事務,你肯定會想到 ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔離性、持久性)這4大特徵,今天我們就來說說其中 I,也就是“隔離性”。
當數據庫上有多個事務同時執行的時候,就可能出現髒讀(dirty read)、不可重複讀(non reapeatable read)、幻讀(phantom read)的問題,爲了解決這些問題,就有了“隔離級 別”的概念。
在談隔離級別之前,你首先要知道,你隔離得越嚴實,效率就會越低。因此很多時候,我們都要在二者之間尋找一個平衡點。SQL 標準的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化 (serializable )。下面我逐一爲你解釋:

1.讀未提交是指,一個事務還沒提交時,它做的變更就能被別的事務看到。
2.讀提交是指,一個事務提交之後,它做的變更纔會被其他事務看到。
3.可重複讀是指,一個事務執行過程中看到的數據,總是跟這個事務在啓動時看到的數據是一致的。當然在可重複讀隔離級別下,未提交變更對其他事務也是不可見的。
4.串行化,顧名思義是對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。

大白話解釋:

讀未提交:別人改數據的事務尚未提交,我在我的事務中也能讀到。
讀已提交:別人改數據的事務已經提交,我在我的事務中才能讀到。
可重複讀:別人改數據的事務已經提交,我在我的事務中也不去讀。
串行:加鎖懂不懂,我的事務尚未提交,別人就別想改數據。

這4種隔離級別,並行性能依次降低,安全性依次提高。

相必大家看完之後都很蒙,下面我用一個例子來解釋一下。設數據 表 T 中只有一列,其中一行的值爲 1,下面是按照時間順序執行兩個事務的行爲。
在這裏插入圖片描述
我們來看看在不同的隔離級別下,事務 A 會有哪些不同的返回結果,也就是圖裏面 V1、V2、 V3 的返回值分別是什麼。

讀提交:V1 = 1,V2  = 2。事務 B 的更新在提交後才能被 A 看到。所以,V3 =  2
可重複讀: V1 = V2 = 1,V3 = 2。之所以 V2 =1,遵循的就是這 個要求:事務在執行期間看到的數據前後必須是一致的。
串行化:所以從 A 的角度看, V1 = V2 = 1,V3 = 2。因爲在事務 B 執行“將 1 改成 2”的時候,會被鎖住。直到事務 A 提交後,事務 B 纔可以繼續執行。

在實現上,數據庫裏面會創建一個視圖,訪問的時候以視圖的邏輯結果爲準。

可重複讀隔離級別:這個視圖是在事務啓動時創建的,整個事務存在期間都用這個視圖。
在讀提交隔離級別:這個視圖是在每個 SQL 語句開始執行的時候創建的。
讀未提交隔離級別:直接返回記錄上的最新值,沒有視圖概念;
串行化隔離級別:直接用加鎖 的方式來避免並行訪問。

我們可以看到在不同的隔離級別下,數據庫行爲是有所不同的。Oracle 數據庫的默認隔離級別其實就是“讀提交”,因此對於一些從 Oracle 遷移到 MySQL 的應用,爲保證數據庫隔離級別的一致,你一定要記得將 MySQL 的隔離級別設置爲“讀提交”。

隔離級別配置方式:
將啓動參數 transaction-isolation 的值設置成 READ-COMMITTED。你可以用 show variables 來查看當前的值。
在這裏插入圖片描述
總結來說,存在即合理,哪個隔離級別都有它自己的使用場景,你要根據自己的業務情況來定。

我想你可能會問那什麼時候需要“可重複讀”的場景呢?我們來看一個數據校對邏輯的案例。
假設你在管理一個個人銀行賬戶表。一個表存了每個月月底的餘額,一個表存了賬單明細。這時候你要做數據校對,也就是判斷上個月的餘額和當前餘額的差額,是否與本月的賬單明細一致。你一定希望在校對過程中,即使有用戶發生了一筆新的交易,也不影響你的校對結果。這時候使用“可重複讀”隔離級別就很方便。事務啓動時的視圖可以認爲是靜態的,不受其他事務更新的影響。

結尾

未完待續…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章