天天看點

深入解析Mysql中事務的四大隔離級别及其所解決的讀現象

深入解析Mysql中事務的四大隔離級别及其所解決的讀現象

本文詳細介紹四種事務隔離級别,并通過舉例的方式說明不同的級别能解決什麼樣的讀現象。并且介紹了在關系型資料庫中不同的隔離級别的實作原理。

在DBMS中,事務保證了一個操作序列可以全部都執行或者全部都不執行(原子性),從一個狀态轉變到另外一個狀态(一緻性)。由于事務滿足久性。是以一旦事務被送出之後,資料就能夠被持久化下來,又因為事務是滿足隔離性的,是以,當多個事務同時處理同一個資料的時候,多個事務直接是互不影響的,是以,在多個事務并發操作的過程中,如果控制不好隔離級别,就有可能産生髒讀、不可重複讀、丢失修改、或者幻讀等讀現象。

在資料庫事務的ACID四個屬性中,隔離性是一個最常放松的一個。可以在資料操作過程中利用資料庫的鎖機制或者多版本并發控制機制擷取更高的隔離等級。但是,随着資料庫隔離級别的提高,資料的并發能力也會有所下降。是以,如何在并發性和隔離性之間做一個很好的權衡就成了一個至關重要的問題。

在軟體開發中,幾乎每類這樣的問題都會有多種最佳實踐來供我們參考,很多DBMS定義了多個不同的“事務隔離等級”來控制鎖的程度和并發能力。

ANSI/ISO SQL定義的标準隔離級别有四種,從高到底依次為:可序列化(Serializable)、可重複讀(Repeatable reads)、送出讀(Read committed)、未送出讀(Read uncommitted)。

下面将依次介紹這四種事務隔離級别的概念、用法以及解決了哪些問題(讀現象)

未送出讀(Read uncommitted)

未送出讀(READ UNCOMMITTED)是最低的隔離級别。通過名字我們就可以知道,在這種事務隔離級别下,一個事務可以讀到另外一個事務未送出的資料。

未送出讀的資料庫鎖情況(實作原理)

事務在讀資料的時候并未對資料加鎖。事務在修改資料的時候隻對資料增加行級共享鎖。

現象:

事務1讀取某行記錄時,事務2也能對這行記錄進行讀取、更新(因為事務一并未對資料增加任何鎖)

當事務2對該記錄進行更新時,事務1再次讀取該記錄,能讀到事務2對該記錄的修改版本(因為事務二隻增加了共享讀鎖,事務一可以再增加共享讀鎖讀取資料),即使該修改尚未被送出。

事務1更新某行記錄時,事務2不能對這行記錄做更新,直到事務1結束。(因為事務一對資料增加了共享讀鎖,事務二不能增加排他寫鎖進行資料的修改)

舉例

事務一 事務二

/ Query 1 / SELECT age FROM users WHERE id = 1; / will read 20 /

/ Query 2 / UPDATE users SET age = 21 WHERE id = 1; / No commit here /

/ Query 1 / SELECT age FROM users WHERE id = 1; / will read 21 /

ROLLBACK; / lock-based DIRTY READ /

下面還是借用我在資料庫的讀現象淺析一文中舉的例子來說明在未送出讀的隔離級别中兩個事務之間的隔離情況。

事務一共查詢了兩次,在兩次查詢的過程中,事務二對資料進行了修改,并未送出(commit)。但是事務一的第二次查詢查到了事務二的修改結果。在資料庫的讀現象淺析中我們介紹過,這種現象我們稱之為髒讀。

是以,未送出讀會導緻髒讀

送出讀(Read committed)

送出讀(READ COMMITTED)也可以翻譯成讀已送出,通過名字也可以分析出,在一個事務修改資料過程中,如果事務還沒送出,其他事務不能讀該資料。

送出讀的資料庫鎖情況(實作原理)

事務對目前被讀取的資料加行級共享鎖(當讀到時才加鎖),一旦讀完該行,立即釋放該行級共享鎖;

事務在更新某資料的瞬間(就是發生更新的瞬間),必須先對其加行級排他鎖,直到事務結束才釋放。

事務1在讀取某行記錄的整個過程中,事務2都可以對該行記錄進行讀取(因為事務一對該行記錄增加行級共享鎖的情況下,事務二同樣可以對該資料增加共享鎖來讀資料。)。

事務1讀取某行的一瞬間,事務2不能修改該行資料,但是,隻要事務1讀取完改行資料,事務2就可以對該行資料進行修改。(事務一在讀取的一瞬間會對資料增加行級共享鎖,任何其他事務都不能對該行資料增加排他鎖。但是事務一隻要讀完該行資料,就會釋放行級共享鎖,一旦鎖釋放,事務二就可以對資料增加排他鎖并修改資料)

事務2更新某行記錄時,事務1不能對這行記錄做讀取或更新,直到事務2結束。(事務2在更新資料的時候,會對該行資料增加排他鎖,直到事務結束才會釋放鎖,是以,在事務2沒有送出之前,事務1都不能對資料增加共享鎖進行資料的讀取。是以,送出讀可以解決髒讀的現象)

/ Query 1 / SELECT * FROM users WHERE id = 1;

/ Query 2 / UPDATE users SET age = 21 WHERE id = 1; COMMIT; / in multiversion concurrency control, or lock-based READ COMMITTED /

/ Query 1 / SELECT FROM users WHERE id = 1; COMMIT; /lock-based REPEATABLE READ */

在送出讀隔離級别中,在事務二送出之前,事務一不能讀取資料。隻有在事務二送出之後,事務一才能讀資料。

但是從上面的例子中我們也看到,事務一在一次事務中兩次讀取的結果并不一緻,是以送出讀不能解決不可重複讀的讀現象。

簡而言之,送出讀這種隔離級别保證了讀到的任何資料都是送出的資料,避免了髒讀(dirty reads)。但是不保證事務重新讀的時候能讀到相同的資料,因為在每次資料讀完之後其他事務可以修改剛才讀到的資料。

可重複讀(Repeatable reads)

可重複讀(REPEATABLE READS),由于送出讀隔離級别會産生不可重複讀的讀現象。是以,比送出讀更高一個級别的隔離級别就可以解決不可重複讀的問題。這種隔離級别就叫可重複讀。

可重複讀的資料庫鎖情況

事務在讀取某資料的瞬間(就是開始讀取的瞬間),必須先對其加行級共享鎖,直到事務結束才釋放;

現象

事務1在讀取某行記錄的整個過程中,事務2都不能修改該行資料(事務1在讀取的整個過程會對資料增加共享鎖,直到事務送出才會釋放鎖,是以整個過程中,任何其他事務都不能對該行資料增加排他鎖。是以,可重複讀能夠解決不可重複讀的讀現象)

事務1更新某行記錄時,事務2不能對這行記錄做讀取、更新,直到事務1結束。(事務1在更新資料的時候,會對該行資料增加排他鎖,知道事務結束才會釋放鎖,是以,在事務2沒有送出之前,事務一都能不對資料增加共享鎖進行資料的讀取。是以,送出讀可以解決髒讀的現象)

/ Query 1 / SELECT * FROM users WHERE id = 1; COMMIT;

在上面的例子中,隻有在事務一送出之後,事務二才能更改該行資料。是以,隻要在事務一從開始到結束的這段時間内,無論他讀取該行資料多少次,結果都是一樣的。

從上面的例子中我們可以得到結論:可重複讀隔離級别可以解決不可重複讀的讀現象。但是可重複讀這種隔離級别中,還有另外一種讀現象他解決不了,那就是幻讀。看下面的例子:

/ Query 1 / SELECT * FROM users WHERE age BETWEEN 10 AND 30;

/ Query 2 / INSERT INTO users VALUES ( 3, 'Bob', 27 ); COMMIT;

上面的兩個事務執行情況及現象如下:

1.事務一的第一次查詢條件是age BETWEEN 10 AND 30;如果這是有十條記錄符合條件。這時,他會給符合條件的這十條記錄增加行級共享鎖。任何其他事務無法更改這十條記錄。

2.事務二執行一條sql語句,語句的内容是向表中插入一條資料。因為此時沒有任何事務對表增加表級鎖,是以,該操作可以順利執行。

3.事務一再次執行SELECT * FROM users WHERE age BETWEEN 10 AND 30;時,結果傳回的記錄變成了十一條,比剛剛增加了一條,增加的這條正是事務二剛剛插入的那條。

是以,事務一的兩次範圍查詢結果并不相同。這也就是我們提到的幻讀。

可序列化(Serializable)

可序列化(Serializable)是最高的隔離級别,前面提到的所有的隔離級别都無法解決的幻讀,在可序列化的隔離級别中可以解決。

我們說過,産生幻讀的原因是事務一在進行範圍查詢的時候沒有增加範圍鎖(range-locks:給SELECT 的查詢中使用一個“WHERE”子句描述範圍加鎖),是以導緻幻讀。

可序列化的資料庫鎖情況(實作原理)

事務在讀取資料時,必須先對其加表級共享鎖 ,直到事務結束才釋放;

事務在更新資料時,必須先對其加表級排他鎖 ,直到事務結束才釋放。

事務1正在讀取A表中的記錄時,則事務2也能讀取A表,但不能對A表做更新、新增、删除,直到事務1結束。(因為事務一對表增加了表級共享鎖,其他事務隻能增加共享鎖讀取資料,不能進行其他任何操作)

事務1正在更新A表中的記錄時,則事務2不能讀取A表的任意記錄,更不可能對A表做更新、新增、删除,直到事務1結束。(事務一對表增加了表級排他鎖,其他事務不能對表增加共享鎖或排他鎖,也就無法進行任何操作)

雖然可序列化解決了髒讀、不可重複讀、幻讀等讀現象。但是序列化事務會産生以下效果:

1.無法讀取其它事務已修改但未送出的記錄。

2.在目前事務完成之前,其它事務不能修改目前事務已讀取的記錄。

3.在目前事務完成之前,其它事務所插入的新記錄,其索引鍵值不能在目前事務的任何語句所讀取的索引鍵範圍中。

四種事務隔離級别從隔離程度上越來越高,但同時在并發性上也就越來越低。之是以有這麼幾種隔離級别,就是為了友善開發人員在開發過程中根據業務需要選擇最合适的隔離級别。

原文位址

https://www.cnblogs.com/kyoner/p/11366805.html