1. 程式人生 > >MySql的隔離級別和鎖的關係

MySql的隔離級別和鎖的關係

               

一、事務的4個基本特徵 

Atomic(原子性): 
事務中包含的操作被看做一個邏輯單元,這個邏輯單元中的操作要 
麼全部成功,要麼全部失敗。 

Consistency(一致性): 
只有合法的資料可以被寫入資料庫,否則事務應該將其回滾到最初 
狀態。 

Isolation(隔離性): 
事務允許多個使用者對同一個資料進行併發訪問,而不破壞資料的正 


確性和完整性。同時,並行事務的修改必須與其他並行事務的修改 
相互獨立。 

Durability(永續性): 
事務結束後,事務處理的結果必須能夠得到固化。 


二、資料庫隔離級別

資料庫事務的隔離級別有4個,由低到高依次為Read uncommittedRead committedRepeatable readSerializable,這四個級別可以逐個解決髒讀不可重複讀幻讀這幾類問題。MySql設定的隔離級別預設為Repeatable Read,可重複讀級別。隔離級別可以配置。


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

髒讀 不可重複讀 幻讀
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

 

注意:我們討論隔離級別的場景,主要是在多個事務併發的情況下,因此,接下來的講解都圍繞事務併發。

Read uncommitted 讀未提交

READ UNCOMMITTED是限制性最弱的隔離級別,因為該級別忽略其他事務放置的鎖。使用READ UNCOMMITTED級別執行的事務,可以讀取尚未由其他事務提交的修改後的資料值,這些行為稱為“髒”讀。我們所說的髒讀,兩個併發的事務,“事務A:領導給singo發工資”、“事務B:singo查詢工資賬戶”,事務B讀取了事務A尚未提交的資料。比如,事務1修改一行,事務2在事務1提交之前讀取了這一行。如果事務1回滾,事務2就讀取了一行沒有提交的資料,這樣的資料我們認為是不存在的。

Read committed 讀提交
該級別通過指定語句不能讀取其他事務已修改但是尚未提交的資料值,禁止執行髒讀。在當前事務中的各個語句執行之間,其他事務仍可以修改、插入或刪除資料,從而產生無法重複的讀操作,或“影子”資料。比如,事務 1 讀取了一行,事務 2 修改或者刪除這一行並且提交。如果事務 1 想再一次讀取這一行,它將獲得修改後的資料或者發現這一樣已經被刪除,因此事務的第二次讀取結果與第一次讀取結果不同,因此也叫不可重複讀。

大多數資料庫的預設級別就是Read committed,比如Sql Server , Oracle。如何解決不可重複讀這一問題,請看下一個隔離級別。

Repeatable read 重複讀
REPEATABLE READ 是比 READ COMMITTED 限制性更強的隔離級別。該級別包括 READ COMMITTED ,並且另外指定了在當前事務提交之前,其他任何事務均不可以 修改或刪除當 前事務已讀取的資料。併發性低於  READ COMMITTED ,因為已讀資料的共享鎖在整個事務期間持有,而不是在每個語句結束時釋放。 這個隔離級別只是說,不能夠修改和刪除,但是並沒有強制不能插入新的滿足條件查詢的資料行 此可以得出結論:REPEATABLE READ隔離級別保證了在相同的查詢條件下,同一個事務中的兩個查詢,第二次讀取的內容肯定包換第一次讀到的內容。注:Mysql的預設隔離級別就是Repeatable read

重複讀與幻讀

重複讀是為了保證在一個事務中,相同查詢條件下讀取的資料值不發生改變,但是不能保證下次同樣條件查詢,結果記錄數不會增加。

幻讀就是為了解決這個問題而存在的,他將這個查詢範圍都加鎖了,所以就不能再往這個範圍內插入資料,這就是SERIALIZABLE 隔離級別做的事情。

Serializable 序列化
SERIALIZABLE  是限制性最強的隔離級別,因為該級別 鎖定整個範圍的鍵 ,並一直持有鎖,直到事務完成。該級別包括 REPEATABLE READ ,並增加了在事務完成之前,其他事務不能向事務已讀取的範圍 插入新行 的限制。比如,事務 1 讀取了一系列滿足搜尋條件的行。事務 2 在執行 SQL statement 產生一行或者多行滿足事務 1 搜尋條件的行時會衝突,則事務 2 回滾。這時事務 1 再次讀取了一系列滿足相同搜尋條件的行,第二次讀取的結果和第一次讀取的結果相同。


三、鎖

一次封鎖or兩段鎖?
因為有大量的併發訪問,為了預防死鎖,一般應用中推薦使用一次封鎖法,就是在方法的開始階段,已經預先知道會用到哪些資料,然後全部鎖住,在方法執行之後,再全部解鎖。這種方式可以有效的避免迴圈死鎖,但在資料庫中卻不適用,因為在事務開始階段,資料庫並不知道會用到哪些資料。
資料庫遵循的是兩段鎖協議,將事務分成兩個階段,加鎖階段和解鎖階段(所以叫兩段鎖)

加鎖階段:在該階段可以進行加鎖操作。在對任何資料進行讀操作之前要申請並獲得S鎖(共享鎖,其它事務可以繼續加共享鎖,但不能加排它鎖),在進行寫操作之前要申請並獲得X鎖(排它鎖,其它事務不能再獲得任何鎖)。加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
解鎖階段:當事務釋放了一個封鎖以後,事務進入解鎖階段,在該階段只能進行解鎖操作不能再進行加鎖操作。
事務                       加鎖/解鎖處理
begin; 
insert into test ..... 加insert對應的鎖
update test set... 加update對應的鎖
delete from test .... 加delete對應的鎖
commit; 事務提交時,同時釋放insert、update、delete對應的鎖
這種方式雖然無法避免死鎖,但是兩段鎖協議可以保證事務的併發排程是序列化(序列化很重要,尤其是在資料恢復和備份的時候)的。

不可重複讀和幻讀的區別
很多人容易搞混不可重複讀和幻讀,確實這兩者有些相似。但不可重複讀重點在於update和delete,而幻讀的重點在於insert

如果使用鎖機制來實現這兩種隔離級別,在可重複讀中,該sql第一次讀取到資料後,就將這些資料加鎖,其它事務無法修改這些資料,就可以實現可重複讀了。但這種方法卻無法鎖住insert的資料,所以當事務A先前讀取了資料,或者修改了全部資料,事務B還是可以insert資料提交,這時事務A就會發現莫名其妙多了一條之前沒有的資料,這就是幻讀,不能通過行鎖來避免。需要Serializable隔離級別 ,讀用讀鎖,寫用寫鎖,讀鎖和寫鎖互斥,這麼做可以有效的避免幻讀、不可重複讀、髒讀等問題,但會極大的降低資料庫的併發能力。

所以說不可重複讀和幻讀最大的區別,就在於如何通過鎖機制來解決他們產生的問題。

上文說的,是使用悲觀鎖機制來處理這兩種問題,但是MySQL、ORACLE、PostgreSQL等成熟的資料庫,出於效能考慮,都是使用了以樂觀鎖為理論基礎的MVCC(多版本併發控制)來避免這兩種問題。

悲觀鎖和樂觀鎖
悲觀鎖
正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)。

在悲觀鎖的情況下,為了保證事務的隔離性,就需要一致性鎖定讀。讀取資料時給加鎖,其它事務無法修改這些資料。修改刪除資料時也要加鎖,其它事務無法讀取這些資料。

樂觀鎖
相對悲觀鎖而言,樂觀鎖機制採取了更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠資料庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是資料庫效能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。

而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基於資料版本( Version )記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 “version” 欄位來實現。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

要說明的是,MVCC的實現沒有固定的規範,每個資料庫都會有不同的實現方式,這裡討論的是InnoDB的MVCC。

MVCC在MySQL的InnoDB中的實現
在InnoDB中,會在每行資料後新增兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行資料何時被建立,另外一個記錄這行資料何時過期(或者被刪除)。 在實際操作中,儲存的並不是時間,而是事務的版本號,每開啟一個新事務,事務的版本號就會遞增。 在可重讀Repeatable reads事務隔離級別下:

  • SELECT時,讀取建立版本號<=當前事務版本號,刪除版本號為空或>當前事務版本號。
  • INSERT時,儲存當前事務版本號為行的建立版本號
  • DELETE時,儲存當前事務版本號為行的刪除版本號
  • UPDATE時,插入一條新紀錄,儲存當前事務版本號為行建立版本號,同時儲存當前事務版本號到原來刪除的行
  • 通過MVCC,雖然每行記錄都需要額外的儲存空間,更多的行檢查工作以及一些額外的維護工作,但可以減少鎖的使用,大多數讀操作都不用加鎖,讀資料操作很簡單,效能很好,並且也能保證只會讀取到符合標準的行,也只鎖住必要行。

我們不管從資料庫方面的教課書中學到,還是從網路上看到,大都是上文中事務的四種隔離級別這一模組列出的意思,RR級別是可重複讀的,但無法解決幻讀,而只有在Serializable級別才能解決幻讀。於是我就加了一個事務C來展示效果。在事務C中添加了一條teacher_id=1的資料commit,RR級別中應該會有幻讀現象,事務A在查詢teacher_id=1的資料時會讀到事務C新加的資料。但是測試後發現,在MySQL中是不存在這種情況的,在事務C提交後,事務A還是不會讀到這條資料。可見在MySQL的RR級別中,是解決了幻讀的讀問題的。參見下圖



Serializable
這個級別很簡單,讀加共享鎖,寫加排他鎖,讀寫互斥。使用的悲觀鎖的理論,實現簡單,資料更加安全,但是併發能力非常差。如果你的業務併發的特別少或者沒有併發,同時又要求資料及時可靠的話,可以使用這種模式。

這裡要吐槽一句,不要看到select就說不會加鎖了,在Serializable這個級別,還是會加鎖的



參考文章:http://blog.csdn.net/fg2006/article/details/6937413

      http://www.cnblogs.com/xwdreamer/archive/2011/01/18/2297042.html

      http://case0079.iteye.com/blog/205201

      http://www.jb51.net/article/75452.htm




           

再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://blog.csdn.net/jiangjunshow