【深刻淺出事務】(3):事務的隔離級別(超詳細)

本質

隔離級別定義了數據庫系統中一個操做產生的影響何時以哪一種方式能夠對其餘併發操做可見,隔離性是事務的ACID中的一個重要屬性,核心是對鎖的操做。html

從數據庫系統角度

  • 共享鎖(Shared Lock)mysql

讀鎖,保證數據只能讀取,不能被修改。
若是事務A對數據M加上S鎖,則事務A能夠讀記錄M但不能修改記錄M,其餘事務(這裏用事務B)只能對記錄M再加上S鎖,不能加X鎖,直到事務A釋放了記錄M上的S鎖,保證了其餘事務(事務B)能夠讀記錄M,但在事務A釋放M上的S鎖以前不能對記錄M進行任何修改。程序員

例子: MySql 5.5 證實S鎖的特性。sql

數據準備數據庫

CREATE TABLE `test1` (`id`  bigint(1) NOT NULL DEFAULT 0 ,`name`  varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,PRIMARY KEY (`id`))
INSERT INTO test1 VALUES(1,1),(2,2),(3,3)
  1. 設置事務爲手動提交,方便證實上訴結論(mysql默認爲自動提交,執行單句sql其實包含 開啓事務,執行sql,提交事務,3個步驟)。
    微信

  2. 客戶端A開啓一個事務A。
    併發

  3. 客戶端A給test1表加上讀鎖。
    性能

  4. 查詢test1表原有的數據(加上S鎖後,能夠讀數據)。
    學習

  5. 事務A修改數據(加上S鎖後,沒法修改數據)。
    spa

  6. B客戶端開啓事務B。

  7. 事務B對記錄M查詢(由於是S鎖,其餘事務能夠對記錄A進行select )。

  8. 事務B對記錄M加讀鎖(事務A對記錄A加上S鎖後,事務B一樣也能夠對記錄A加上S鎖,證實了,mysql裏的讀鎖就是S鎖,具備共享)。

  9. 事務B對記錄M加寫鎖(一直處於等待狀態,被掛起,具體掛起的緣由能夠見:Innodb行鎖源碼學習

  10. 事務B對記錄M修改(一直處於等待狀態,被掛起)。

  11. 事務A釋放M上的S鎖。

此時事務B才獲得響應。

說明了,只有釋放了讀鎖,另一個事務才能加寫鎖,或者更新數據。

  • 排他鎖(X 鎖)

寫鎖,若事務A對數據對象M加上X鎖,事務A能夠讀記錄M也能夠修改記錄M,其餘事務(事務B)不能再對記錄M加任何鎖,直到事務A釋放記錄M上的鎖,保證了其餘事務(事務B)在事務A釋放記錄M上的鎖以前不能再讀取和修改記錄M。

例子:Mysql 5.5,證實X鎖的特性。

  1. 客戶端A設置手動提交,而且開啓事務A。

  2. 客戶端B設置手動提交,而且開啓事務B。

  3. 事務A給記錄M加上X鎖。

  4. 事務A能夠讀記錄M也能夠修改記錄M。

  5. 事務B不能對記錄M加任何鎖。

  6. 事務B也不能對記錄M進行查詢和修改。

  7. 事務A釋放記錄M上的X鎖。

  8. 事務B阻塞的進程被執行,中斷了6秒。

從程序員角度

  • 悲觀鎖(Pessimistic[ˌpesɪˈmɪstɪk] Lock)

對數據被外界修改保持保守態度,在整個數據處理過程當中,數據處於鎖定狀態,依賴於數據庫提供的鎖機制。

  • 樂觀鎖(Optimistic[ˌɑ:ptɪˈmɪstɪk]Lock)

採用寬鬆的加鎖機制,基於數據版本記錄機制,具體作法:數據庫表增長一個"version"字段來實現,讀取數據時,將版本號一同讀出,以後更新,對版本號加1,將提交數據的版本數據與數據庫對應記錄的當前版本信息進行比對,若是提交的數據版本號大於數據庫的數據,則予以更新,不然,被認爲是過時數據。

幾個概念

丟失更新

事務A和事務B,同時得到相同數據,而後在各自的事務中修改數據M,事務A先提交事務,數據M假如爲M+,事務B後提交事務,數據M變成了M++,最終結果變成M++,覆蓋了事務A的更新。

例子:

事務A 事務B
讀取X=100 讀取X=100
寫入X=X+100
事務結束X=200
寫入X=X+100
事務結束X=300(事務A的更新丟失)

髒讀

容許事務B能夠讀到事務A修改而未提交的數據可能會形成了髒讀(髒讀本質就是無效的數據,只有當事務A回滾,那麼事務B讀到的數據才爲無效的,因此這裏只是可能形成髒讀,當事務A不回滾的時候,事務B讀到的數據就不爲髒數據,也就是有效的數據,髒數據會致使之後的操做都會發生錯誤,必定要去避免,不能憑藉僥倖,事務A不能百分之百保證不回滾,因此這種隔離級別不多用於實際應用,而且它的性能也不比其餘級別好多少)。

例子:

事務A 事務B
寫入X=X+100(x=200)
讀取X=200(無效數據,髒讀)
事務回滾X=100
事務結束X=100
事務結束

不可重複讀

不可重複讀是指在一個事務範圍中2次或者屢次查詢同一數據M返回了不一樣的數據,例如:事務B讀取某一數據,事務A修改了該數據M而且提交,事務B又讀取該數據M(多是再次校驗),在同一個事務B中,讀取同一個數據M的結果集不一樣,這個很蛋疼。

例子:

事務A 事務B
讀取X=100 讀取X=100
寫入X=X+100 讀取X=100
事務結束,X=200
讀取X=200(在一個事務B中讀X的值發生了變化)
事務結束

幻讀

當用戶讀取某一個範圍的數據行時,另外一個事務又在該範圍內查詢了新行,當用戶再讀取該範圍的數據行時,會發現會有新的「幻影行」,例如:事務B讀某一個數據M,事務A對數據M增長了一行並提交,事務B又讀數據M,發生多出了一行形成的結果不一致(若是行數相同,則是不可重複讀)。

例子:

事務A 事務B
讀取數據集M(3行)
在數據集M插入一行(4行)
事務結束
讀取數據M(4行)
事務結束

在事務B裏,同一個數據集M,讀到的條數不一致(新增,刪除)。

封鎖協議

在運用S鎖和X鎖對數據M加鎖的時候,須要約定一些規則,例如什麼時候申請S鎖或者X鎖,持鎖時間,這些規則就是封鎖協議。
其中不一樣的封鎖協議對應不一樣的隔離級別。

一級封鎖協議

一級封鎖協議對應READ-UNCOMMITTED 隔離級別,本質是在事務A中修改完數據M後,馬上對這個數據M加上共享鎖(S鎖)[當事務A繼續修改數據M的時候,先釋放掉S鎖,再修改數據,再加上S鎖],根據S鎖的特性,事務B能夠讀到事務A修改後的數據(不管事務A是否提交,由於是共享鎖,隨時隨地都能查到數據A修改後的結果),事務B不能去修改數據M,直到事務A提交,釋放掉S鎖。

缺點:
可能會形成以下後果

  1. 丟失更新。

  2. 髒讀。

  3. 不可重複讀。

  4. 幻讀。

例子:MySql 5.5 證實一級封鎖協議會形成髒讀,不可重複讀。

A客戶端修改數據M,B客戶端設置不一樣的隔離級別去查看數據M,論證該級別下會發生髒讀不可重複讀(至關於客戶端A修改的數據已經寫到表裏,客戶端B傳不一樣版本號[隔離級別],去查看數據M,所得的查詢結果也不一樣)。

  1. 客戶端A設置手動提交,而且開啓事務A。

  2. 客戶端B設置手動提交,修改事務隔離級別爲read-uncommitted,而且開啓事務B(必定要在開啓事務前修改事務的隔離級別,否則當前仍是保持着原來的事務隔離級別,直到當前事務提交)

  3. 事務B查詢數據M。

  4. 事務A修改其中一行數據(查詢原有基礎數據,而後把id = 1 的name 修改成4 )。

  5. 事務B查看數據M,發現事務B讀到了事務A未提交的數據,發生了髒讀。

  6. 事務A回滾。

  7. 客戶端B查詢的狀況。

在同一個事務B裏,查詢同一個數據M,竟然2次不同,形成不可重複讀,其中有一次數據是無效的數據,髒讀了。
假如事務A不回滾呢? 那麼事務B就沒形成髒讀,不可重複讀。

例子:MySql 5.5 證實一級封鎖協議會形成更新丟失

  1. 事務A提交數據M。

  2. 事務B查詢數據M,事務B查詢的數據M,沒有髒數據,而且2次結果一致,沒出現不可重複讀。

  3. 事務B修改數據M。

此時事務A對數據M的修改被事務B給覆蓋,形成了更新丟失

例子:MySql 5.5 證實一級封鎖協議會形成幻讀

  1. 客戶端A設置手動提交,而且開啓事務A。

  2. 客戶端B設置手動提交,修改事務隔離級別爲read-uncommitted,而且開啓事務B(必定要在開啓事務前修改事務的隔離級別,否則當前仍是保持着原來的事務隔離級別,直到當前事務提交)

  3. 事務B查詢數據M。

  4. 事務A插入一條數據。

  5. 事務B查詢數據M。

事務B第二次查詢的時候,數據M多了一行,像是發生了幻覺似的,有可能這一行是無效數據(當事務A回滾)。

二級封鎖協議

二級封鎖協議對應READ-COMMITTED隔離級別,本質是事務A在修改數據M後馬上加X鎖,事務B不能修改數據M,同時不能查詢到最新的數據M(避免髒讀),查詢到的數據M是上一個版本(Innodb MVCC快照)的。

優勢:
1.避免髒讀。

缺點:
可能會形成以下後果

  1. 丟失更新。

  2. 不可重複讀。

  3. 幻讀。

例子:MySql 5.5 證實二級封鎖協議不會形成髒讀,可是會形成不可重複讀(幻讀,丟失更新,和上面證實方式同樣,這裏暫不證實了)
A客戶端修改數據M,B客戶端設置不一樣的隔離級別去查看數據M,論證該級別下會發生不可重複讀(至關於客戶端A修改的數據已經寫到表裏,客戶端B傳不一樣版本號[隔離級別],去查看數據M,所得的查詢結果也不一樣)。

  1. 客戶端A設置手動提交,而且開啓事務A。

  2. 客戶端B設置手動提交,修改事務隔離級別爲read-committed,而且開啓事務B(必定要在開啓事務前修改事務的隔離級別,否則當前仍是保持着原來的事務隔離級別,直到當前事務提交)

  3. 事務A修改其中一行數據(查詢原有基礎數據,而後把id = 1 的name 修改成4 )。

  4. 事務B查詢數據M,數據仍是和以前的同樣(沒有發生髒讀),事務B讀不到了事務A未提交的數據。

  5. 事務A提交。

  6. 事務B查詢數據,數據M被修改。

在同一個事務B中,查詢數據M,2次結果不一致,證實發生了不可重複讀。

三級封鎖協議

三級封鎖協議對應REPEATABLE-READ隔離級別,本質是二級封鎖協議基礎上,對讀到的數據M瞬間加上共享鎖,直到事務結束才釋放(保證了其餘事務沒辦法修改該數據),這個級別是MySql 5.5 默認的隔離級別。

優勢:
1.避免髒讀。
2.避免不可重複讀。

缺點:

  1. 幻讀。

  2. 丟失更新。

例子:MySql 5.5 證實三級封鎖協議不會形成髒讀,不可重複讀(形成幻讀,丟失更新,和上面證實方式同樣,可是要在非mysql數據庫上證實,後面有解釋,這裏暫不證實了)

  1. 客戶端A設置手動提交,而且開啓事務A。

  2. 客戶端B設置手動提交,修改事務隔離級別爲repeatable-read,而且開啓事務B(必定要在開啓事務前修改事務的隔離級別,否則當前仍是保持着原來的事務隔離級別,直到當前事務提交)

  3. 事務A查詢數據M。

  4. 事務B查詢數據M。

  5. 事務A更新數據M。

  6. 事務B查詢數據M,發現查詢的結果沒變化,避免了髒讀。

  7. 事務A提交事務。

  8. 事務B查詢數據M,仍是和以前查詢的結果同樣,沒有變化,避免了不可重複讀

  9. 事務B提交事務

這個時候事務B才能查詢到最新的數據M+。

例子:MySql 5.5 證實Mysql Innodb引擎的三級封鎖協議不會形成幻讀

mysql innodb的reapetable read級別是避免了幻讀,mysql的實現和標準定義的RR隔離級別有差異,詳情見 how-to-produce-phantom-reads

  1. 客戶端A設置手動提交,而且開啓事務A。

  2. 客戶端B設置手動提交,修改事務隔離級別爲repeatable-read,而且開啓事務B(必定要在開啓事務前修改事務的隔離級別,否則當前仍是保持着原來的事務隔離級別,直到當前事務提交)

  3. 事務A查詢數據M。

  4. 事務B查詢數據M。

  5. 事務A對記錄M插入一條數據。

  6. 事務A提交。

  7. 事務B查看數據M。

看不到事務A新增長的一條數據,說明避免了幻讀。

  1. 事務B插入一條記錄。

明明剛剛查詢到沒有ID爲4的,如今竟然插不進去,哈哈哈哈哈。

例子:MySql 5.5 證實三級封鎖協議在讀到數據的瞬間加上共享鎖,等事務結束才釋放以及三級封鎖協議會形成更新丟失

  1. 客戶端A從新開啓事務A。

  2. 客戶端B設置手動提交,修改事務隔離級別爲read-committed,而且開啓事務B(必定要在開啓事務前修改事務的隔離級別,否則當前仍是保持着原來的事務隔離級別,直到當前事務提交)

  3. 事務A修改數據M。

  4. 事務B也修改數據M,事務B的修改進程被掛起,由於事務A在對數據M修改後瞬間加上了共享鎖,對於其餘事務只能讀。

  5. 事務A提交事務。

  6. 事務B的修改進程被喚起(等待4.98秒)。

  7. 事務B提交修改。

  8. 最終事務A,事務B查詢數據M。

事務B的修改把事務A的修改給覆蓋了,形成了更新丟失。

最強封鎖協議

最強封鎖協議對應Serialization隔離級別,本質是從MVCC併發控制退化到基於鎖的併發控制,對事務中全部讀取操做加S鎖,寫操做加X鎖,這樣能夠避免髒讀,不可重複讀,幻讀,更新丟失,開銷也最大,會形成讀寫衝突,併發程度也最低。

例子:MySql 5.5 證實三級封鎖協議不會形成幻讀

  1. 客戶端A從新開啓事務A

  2. 客戶端B設置手動提交,修改隔離級別爲SERIALIZABLE,而且開啓事務B。

  3. 事務A插入數據,而且提交。

  4. 事務B查詢數據,依然仍是以前的數據,避免的幻讀。

  5. 事務A修改數據,被掛起。

證實了Serialization級別下寫操做是對數據M加的是X鎖。

總結

ANSI SQL 隔離級級別

隔離性 髒讀可能性 不可重複讀可能性 幻讀可能性 加鎖讀
READ-UNCOMMITTED Y Y Y N
READ-COMMITTED N Y Y N
REPEATABLE-READ N N Y N
SERIALIZABLE N N N Y

感謝您的耐心閱讀,若是您發現文章中有一些沒表述清楚的,或者是不對的地方,請給我留言,您的鼓勵是做者寫做最大的動力,
若是您認爲本文質量不錯,讀後以爲收穫很大,不妨請我喝杯咖啡,讓我更有動力繼續寫出高質量的文章。

  • 支付寶

  • 微信

做 者 : @mousycoder

原文出處 : http://mousycoder.com/2016/02/19/explain-transaction-in-simple-language-3/

創做時間:2016-2-19

更新時間:2016-2-22

相關文章
相關標籤/搜索