天天看點

MySql 事物的四種隔離級别

事務的 四個特征(ACID)

事務具有四個特征:原子性( Atomicity )、一緻性( Consistency )、隔離性( Isolation )和持續性( Durability )。這四個特性簡稱為 ACID 特性。

1 、原子性。事務是資料庫的邏輯工作機關,事務中包含的各操作要麼都做,要麼都不做

2 、一緻性。事 務執行的結果必須是使資料庫從一個一緻性狀态變到另一個一緻性狀态。是以當資料庫隻包含成功事務送出的結果時,就說資料庫處于一緻性狀态。如果資料庫系統 運作中發生故障,有些事務尚未完成就被迫中斷,這些未完成事務對資料庫所做的修改有一部分已寫入實體資料庫,這時資料庫就處于一種不正确的狀态,或者說是 不一緻的狀态。

3 、隔離性。一個事務的執行不能其它事務幹擾。即一個事務内部的操作及使用的資料對其它并發事務是隔離的,并發執行的各個事務之間不能互相幹擾。

4 、持續性。也稱永久性,指一個事務一旦送出,它對資料庫中的資料的改變就應該是永久性的。接下來的其它操作或故障不應該對其執行結果有任何影響。

MySql的四種隔離級别

SQL标準定義了4類隔離級别,包括了一些具體規則,用來限定事務内外的哪些改變是可見的,哪些是不可見的。低級别的隔離級一般支援更高的并發處理,并擁有更低的系統開銷。

Read Uncommitted(讀取未送出内容)

在該隔離級别,所有事務都可以看到其他未送出事務的執行結果。本隔離級别很少用于實際應用,因為它的性能也不比其他級别好多少。讀取未送出的資料,也被稱之為髒讀(Dirty Read)。

Read Committed(讀取送出内容)

這是大多數資料庫系統的預設隔離級别(但不是MySQL預設的)。它滿足了隔離的簡單定義:一個事務隻能看見已經送出事務所做的改變。這種隔離級别 也支援所謂的不可重複讀(Nonrepeatable Read),因為同一事務的其他執行個體在該執行個體處理其間可能會有新的commit,是以同一select可能傳回不同結果。

Repeatable Read(可重讀)

這是MySQL的預設事務隔離級别,它確定同一事務的多個執行個體在并發讀取資料時,會看到同樣的資料行。不過理論上,這會導緻另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當使用者讀取某一範圍的資料行時,另一個事務又在該範圍内插入了新行,當使用者再讀取該範圍的資料行時,會發現有新的“幻影” 行。InnoDB和Falcon存儲引擎通過多版本并發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。

Serializable(可串行化)

這是最高的隔離級别,它通過強制事務排序,使之不可能互相沖突,進而解決幻讀問題。簡言之,它是在每個讀的資料行上加上共享鎖。在這個級别,可能導緻大量的逾時現象和鎖競争。

出現問題

這四種隔離級别采取不同的鎖類型來實作,若讀取的是同一個資料的話,就容易發生問題。例如:

髒讀(Drity Read):某個事務已更新一份資料,另一個事務在此時讀取了同一份資料,由于某些原因,前一個RollBack了操作,則後一個事務所讀取的資料就會是不正确的。

不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中資料不一緻,這可能是兩次查詢過程中間插入了一個事務更新的原有的資料。

幻讀(Phantom Read):在一個事務的兩次查詢中資料筆數不一緻,例如有一個事務查詢了幾列(Row)資料,而另一個事務卻在此時插入了新的幾列資料,先前的事務在接下來的查詢中,就會發現有幾列資料是它先前所沒有的。

在MySQL中,實作了這四種隔離級别,分别有可能産生問題如下所示:

MySql 事物的四種隔離級别

測試Mysql的隔離級别

下面,将利用MySQL的用戶端程式,我們分别來測試一下這幾種隔離級别。

測試資料庫為demo,表為test;表結構:

MySql 事物的四種隔離級别

兩個指令行用戶端分别為A,B;不斷改變A的隔離級别,在B端修改資料。

(一)、将A的隔離級别設定為read uncommitted(未送出讀)

set session transaction isolation level read uncommitted;

檢視隔離級别是否設定成功

select @@transaction_isolation (mysql版本 8.0 以後)

select @@tx_isolation (mysql版本 8.0 之前)

檢視mysql版本

> status

A:啟動事務,此時資料為初始狀态

start transaction;

B:啟動事務,更新資料,但不送出

start transaction;

A:再次讀取資料,發現資料已經被修改了,這就是所謂的“髒讀”

MySql 事物的四種隔離級别

B:復原事務

rollback;

A:再次讀資料,發現資料變回初始狀态

MySql 事物的四種隔離級别

經過上面的實驗可以得出結論,事務B更新了一條記錄,但是沒有送出,此時事務A可以查詢出未送出記錄。造成髒讀現象。未送出讀是最低的隔離級别。

(二)、将用戶端A的事務隔離級别設定為read committed(已送出讀)

set session transaction isolation level read committed;

A:啟動事務,此時資料為初始狀态

B:啟動事務,更新資料,但不送出

A:再次讀資料,發現資料未被修改

B:送出事務

A:再次讀取資料,發現資料已發生變化,說明B送出的修改被事務中的A讀到了,這就是所謂的“不可重複讀”

經過上面的實驗可以得出結論,已送出讀隔離級别解決了髒讀的問題,但是出現了不可重複讀的問題,即事務A在兩次查詢的資料不一緻,因為在兩次查詢之間事務B更新了一條資料。已送出讀隻允許讀取已送出的記錄,但不要求可重複讀。

(三)、将A的隔離級别設定為repeatable read(可重複讀)

A:啟動事務,此時資料為初始狀态

B:啟動事務,更新資料,但不送出

A:再次讀取資料,發現資料未被修改

B:送出事務

A:再次讀取資料,發現資料依然未發生變化,這說明這次可以重複讀了

B:插入一條新的資料,并送出

A:再次讀取資料,發現資料依然未發生變化,雖然可以重複讀了,但是卻發現讀的不是最新資料,這就是所謂的“幻讀”

A:送出本次事務,再次讀取資料,發現讀取正常了

由以上的實驗可以得出結論,可重複讀隔離級别隻允許讀取已送出記錄,而且在一個事務兩次讀取一個記錄期間,其他事務部的更新該記錄。但該事務不要求與其他事務可串行化。例如,當一個事務可以找到由一個已送出事務更新的記錄,但是可能産生幻讀問題(注意是可能,因為資料庫對隔離級别的實作有所差别)。像以上的實驗,就沒有出現資料幻讀的問題

(四)、将A的隔離級别設定為可串行化(Serializable)

A:啟動事務,此時資料為初始狀态

B:發現B此時進入了等待狀态,原因是因為A的事務尚未送出,隻能等待(此時,B可能會發生等待逾時)

A:送出事務

B:發現插入成功

serializable完全鎖定字段,若一個事務來查詢同一份資料就必須等待,直到前一個事務完成并解除鎖定為止。是完整的隔離級别,會鎖定對應的資料表格,因而會有效率的問題。

繼續閱讀