1. 程式人生 > >InnoDB 事務隔離級別(Mysql篇)

InnoDB 事務隔離級別(Mysql篇)

前言:
Mysql支援MyISAM和InnoDB兩種儲存引擎,區別在此就不詳細說明。此篇是講述事務,所以切記自己的table是InnDB。此處大坑!

在Mysql InnoDB 中,事務主要有四種隔離級別

  • Read uncommitted (未提交讀)
  • Read committed (已提交讀)
  • Repeatable read (可重複讀)
  • Serializable (可序列化)

在理解四種隔離級別之前,我們需要先了解另外三個名詞:

  1. 髒讀
  2. 不可重複讀
  3. 幻讀

髒讀

另一個事務修改了資料,但尚未提交,而本事務中的SELECT會讀到這些未被提交的資料。

不重複讀

解決了髒讀後,會遇到,同一個事務執行過程中,另外一個事務提交了新資料,因此本事務先後兩次讀到的資料結果會不一致。

幻讀

解決了不重複讀,保證了同一個事務裡,查詢的結果都是事務開始時的狀態(一致性)。但是,如果另一個事務同時提交了新資料,本事務再更新時,就會“驚奇的”發現了這些新資料,貌似之前讀到的資料是“鬼影”一樣的幻覺。

下面我們就直接來通過實驗來看,Mysql Innodb中,不同的事務隔離級別,會出現怎麼樣的結果。

首先我們開啟兩個終端,查詢當前MySQL的預設隔離級別:

SELECT @@global.tx_isolation; //查詢全域性事務
SELECT
@@session.tx_isolation;
//查詢當前會話事務

這裡寫圖片描述
可以看到,預設的隔離級別是:REPEATABLE-READ

實驗Read uncommitted

我們將會話事務設定為:Read uncommitted

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
//測試可以不用設定全域性事務
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;(這個可以不用設,只設置上面一行就可以了進行測試了)

更改完之後,重新查詢事務:

這裡寫圖片描述
可以看到,全域性事務已經更改為Read uncommitted

然後,我們首先建立一個測試的資料庫test_tx,並插入了2條測試資料,如下圖:

這裡寫圖片描述
然後我們分別開啟事務,然後我們在B終端中,插入一條資料,但是不提交,然後在A終端進行資料查詢。

這裡寫圖片描述

可以看到,我們在B終端insert一條資料,但是未進行提交操作(commit),但是在A事務中,卻查詢到了。我們稱這種現象叫做髒讀,在實際開發過程中,我們一般較少使用Read uncommitted隔離級別,這種隔離級別對任何的資料操作都不會進行加鎖。

實驗Read committed

首先我們將會話的事務隔離級別設定為read committed

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

然後我們用上面相同的方式,進行測試。首先同時將2個終端的事務開啟:begin;,然後在B終端中插入一條新的資料insert into test_tx values(4,”Lee”);,但是不提交事務(commit),然後在A終端中,查詢資料,如圖,我們在A終端中,沒有查詢到剛才插入的這條資料。

這裡寫圖片描述

所以,實驗表明,在Read committed隔離級別,不會出現髒讀的問題。

然後我們繼續做實驗,看看在Read committed隔離級別中,會不會出現不可重複讀、幻讀的現象。

我們同時開啟兩個終端的事務,然後在A終端中,查詢當前的資料,然後我們在B終端中,將ID為3的資料,name修改為Jeff。然後將B終端的事務提交(commit),但是A終端不提交事務,在一個事務的生命週期內,然後查詢資料,我們查詢到了剛才B終端修改過的資料。也就是說,我們在A終端的一個事務週期內(事務未commit),兩次查詢,得到的結果是不一樣的。

實驗表明,在Read committed隔離級別中,存在不可重複讀的現象。

我們繼續做實驗,因為剛才B終端已經將事務提交,所以我們重新開啟B終端的事務,然後我們在B終端中,插入(insert)一條ID為5的新資料,並提交事務。然後我們回到A終端,查詢資料,我們同樣可以查詢到剛才B終端新插入的資料。也就是說我們在A終端中,三次查詢,得到的結果都是不一樣的。
這裡寫圖片描述

實驗表明,在Read committed隔離級別中,存在幻讀的現象。

總結,在Read committed隔離級別中,可以有效解決髒讀問題,但是有不可重複讀、幻讀問題,而不可重複讀和幻讀的差異主要是,不可重複讀主要是針對修改和刪除操作、幻讀針對插入資料操作。

實驗Repeatable read

首先我們將隔離級別更改為Repeatable read

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

然後我們首先實驗,在Repeatable read級別中是否存在髒讀問題,我們首先同時開啟A,B兩個終端的事務(begin;),然後在B終端中,插入一條ID為6的資料,但是不提交事務。然後在A終端中進行資料查詢,結果是我們未查詢到剛才插入的資料,所以在Repeatable read級別中,沒有髒讀現象。
這裡寫圖片描述

接著,我們順著剛才的新插入的資料,然後將B終端的事務進行提交,然後再回到A終端查詢資料,依然沒有查詢到B終端剛才插入的ID為6的資料,以此也就表明,目前Mysql 5.6以上的版本中,Repeatable read級別已經不存在幻讀的問題,而之前的版本我並未做測試,後面有時間會在去查一下,mysql是在哪個版本開始解決了幻讀問題。

這裡寫圖片描述
由於剛才B終端已經提交了事務,所以為了實驗是否存在不可重複讀的現象,我們重新開啟B終端的事務,然後我們將ID為5的name修改為Joy:update test_tx set name = "Joy" where id = 5;,同時B終端的事務commit;,然後我們回到A終端進行查詢,三次的查詢結果都是一致的。所以實驗表明,在Repeatable read級別中,不存在不可重複讀現象。

這裡寫圖片描述
總結,在Repeatable read級別中,髒讀、不可重複讀、幻讀現象都沒有。在mysql中,該級別也是預設的事務隔離級別,我們日常在開發中,也是主要使用該隔離級別。

Serializable

Serializable完全序列化的讀,每次讀都需要獲得表級共享鎖,讀寫相互會相互互斥,這樣可以更好的解決資料一致性的問題,但是同樣會大大的降低資料庫的實際吞吐效能。所以該隔離級別因為損耗太大,一般很少在開發中使用。

總結:

隔離級別 髒讀 不可重複讀 幻讀
未提交讀(Read uncommitted) 可能 可能 可能
已提交讀(Read committed) 不可能 可能 可能
可重複讀(Repeatable read) 不可能 不可能 不可能
可序列化(Serializable ) 不可能 不可能 不可能