MySQL隔離級別和封鎖協議

2021-08-18 01:53:43 字數 3200 閱讀 9007

一直以來對資料庫的事務隔離機制的理解總是停留在表面,其內容也是看一遍忘一邊。這兩天決定從原理上理解它,整理成自己的知識。查閱資料的過程中發現好多零碎的概念如果串起來足夠寫一本書,所以在這裡給自己梳理乙個脈絡,具體的內容參考引文或在網上搜一下。由於平時接觸最多的是mysql,所以文章中某些部分是mysql特有的特性,請讀者注意。

資料庫併發操作會引發的問題:

多個事務同時訪問資料庫時候,會發生下列5類問題,包括3類資料讀問題(髒讀,不可重複讀,幻讀),2類資料更新問題(第一類丟失更新,第二類丟失更新):

髒讀(dirty read):a事務讀取b事務尚未提交的更改資料,並在這個資料基礎上操作。如果b事務回滾,那麼a事務讀到的資料根本不是合法的,稱為髒讀。在oracle中,由於有version控制,不會出現髒讀。

不可重複讀(unrepeatable read):a事務讀取了b事務已經提交的更改(或刪除)資料。比如a事務第一次讀取資料,然後b事務更改該資料並提交,a事務再次讀取資料,兩次讀取的資料不一樣。

幻讀(phantom read):a事務讀取了b事務已經提交的新增資料。注意和不可重複讀的區別,這裡是新增,不可重複讀是更改(或刪除)。這兩種情況對策是不一樣的,對於不可重複讀,只需要採取行級鎖防止該記錄資料被更改或刪除,然而對於幻讀必須加表級鎖,防止在這個表中新增一條資料。

第一類丟失更新:a事務撤銷時,把已提交的b事務的資料覆蓋掉。

第二類丟失更新:a事務提交時,把已提交的b事務的資料覆蓋掉。

資料庫在併發操作下會出現上述這些問題,要解決它就要想辦法在執行可能引發問題的操作之前將該操作阻塞住,讓它等到合適的時機再執行。那麼如何挑選合適的時機阻塞操作的執行,又如何保證在排程過程執行完成後其執行結果與序列執行操作的結果相同呢?

**封鎖協議

資料庫想要在「合適」的時機阻塞住資料庫操作,那麼首先要定義好怎麼樣的時機算是「合適」,因為各個系統支援的業務千差萬別,對資料的實時性和有效性的要求也不同。於是資料庫理論中就提出了封鎖級別的概念,對不同的同步要求採用不同的封鎖級別。

**封鎖協議內容如下:

事務隔離級別:

**封鎖協議反映在實際的資料庫系統上,就是四級事務隔離機制。總的來說,四種事務隔離機制就是在逐漸的限制事務的自由度,以滿足對不同併發控制程度的要求。以下就是資料庫的四種隔離級別:

read uncommitted、read committed、repeatable read、serializable

其對各個併發問題的制約強度見下表:

√: 可能出現    ×: 不會出現

髒讀不可重複讀

幻讀read uncommitted√√

√read committed×√

√repeatable read××

√serializable××

× 四種級別對併發問題的解決由弱到強,相應的系統效能由強到弱,mysql的預設級別是repeatable read。

read uncommitted

在read uncommitted策略下,資料庫遵循一級封鎖協議,只對修改資料的併發操作做限制。乙個事務不能修改其他事務正在修改的資料,但可以讀取到其他事務中尚未提交的修改,這些修改如果未被提交,將會成為髒資料。

read committed

在read committed策略下,資料庫遵循二級封鎖協議,只允許讀取已經被提交的資料,反過來講,如果乙個事務修改了某行資料且尚未提交,而第二個事務要讀取這行資料的話,那麼是不允許的。在mysql的innodb下,雖然這種操作不被允許,但mysql不會阻塞住資料的查詢操作,而是會查詢出資料被修改之前的備份,返回給客戶端。mysql的這種機制稱為mvcc(多版本併發控制),就是說資料庫在事務併發的過程中對資料維護多個版本,使得不同的事務對不同的資料版本進行讀寫(mvcc的實現參見引用中的文章)。這樣的機制反映在應用中就是,在任何時候對資料庫查詢總是可以得到資料庫中最近提交的資料。為被提交的髒資料被隔離起來,無法被查詢到,即防止髒讀發生。

repeat read

repeat read又比read committed更加嚴格一點,但仍然是在二級封鎖協議的範疇,只是讀取過程受到更多mvcc的影響。在read committed下,允許乙個事務中多次相同查詢得到不同的結果,就是所謂的不可重複讀問題。這在一些應用中是允許的,所以oracle、sql server上預設這一隔離級別,但mysql沒有,它預設repeat read級別。在這一級別下,有賴於mvcc,同乙個事務中的查詢只能查到版本號不高於當前事務版本的資料,即事務只能看到該事務開始前或者被該事物影響的資料。反過來說,這一級別下,不允許事務讀取在該事務開始後新提交的資料。即防止了不可重複讀的發生。

依靠上面的機制,已經做到了在事務內資料內容的不變,但是不能保證多次查詢得到的資料數量一致。因為在乙個事務執行的過程中別的事務完全可以執行資料插入,當插入了剛好符合查詢條件的資料時,就會引發資料查詢結果集增加,引發幻讀。還有一種情況就是,如果乙個事務想插入一條資料,而另乙個事務已經插入了含有相同主鍵的資料,那麼當前事務也會被阻塞,並最終執行失敗,雖然當前事務根本無法查詢到這一條資料,這也是一種幻讀。innodb提供的間隙鎖機制可以在一定程度上防止幻讀的發生,具體介紹見最後一篇引文。

serializable

最後,最強事務隔離機制serializable,它遵循**封鎖協議,使得所有的事務必須序列化執行,只要有事務在對錶進行查詢,那麼在此事務提交前,任何其他事務的修改都會被阻塞。這解決了一切併發問題,但會造成大量的等待、阻塞甚至死鎖,使系統效能降低。

要注意,在任何一種隔離機制下,都是不允許乙個事務刪除或修改另乙個事務影響過而未提交的資料的。因為事務增、刪、改資料以後,會在該行加上排它鎖,排它鎖會阻塞其他事務再次對該行資料操作。也正是由於排它鎖的存在,這四種隔離機制都不會出現任何一種更新丟失的現象,因為一條資訊根本不允許第二個事務進行修改。

兩段鎖協議

資料庫在排程併發事務時遵循「兩段鎖」協議,「兩段鎖」協議是指所有事務必須分兩個階段對資料項進行加鎖和解鎖

擴充套件階段:在對任何資料項的讀、寫之前,要申請並獲得該資料項的封鎖。

收縮階段:每個事務中,所有的封鎖請求必須先於解鎖請求。

在數學上可以證明,遵循兩段鎖的排程可以保證排程結果與序列化排程相同。這樣的機制保證了資料庫併發排程與序列排程的等價。

*注:

資料庫隔離級別 對應封鎖協議

1.讀未提交 read uncommit 一級封鎖協議 讀取資料的時候不加鎖,更新的時候整個加x鎖 b事物執行到一半,a事物不檢測鎖直接讀取,結果b事物回滾了,導致a事物讀出了乙個錯的結果,這就是髒讀。2.讀已提交 read committed 二級封鎖協議 讀取資料的時候加s鎖,更新的時候加x鎖 ...

mysql隔離級別 MySQL 事務隔離級別

mysql innodb所提供的事務滿足acid的要求,事務是通過事務日誌中的redo log和undo log來實現原子性 undo log 一致性 undo log 永續性 redo log 事務通過鎖機制實現隔離性。1 事務隔離級別與實現read uncommitted 讀未提交 read c...

mysql隔離級別驗證 mysql 隔離級別測試

1 設定隔離級別,包括 全域性 global.tx isolation,會話級別 session.tx isolation mysql workbench的會話 2 建立測試表 注意 unsigned auto increment primary key用法 預設引擎的設定 建立測試表 use te...