MySQL事務隔離級別詳解

到了四種隔離級別,當時以爲大多數數據庫都爲read committed,結果沒想到mysql是個例外。在此作一下隔離級別和各類數據庫鎖的使用。mysql

首先說一下ACID四大特性:算法

四大特性

      · 原子性
  事務必須是原子工做單元;對於其數據修改,要麼全都執行,要麼全都不執行。一般,與某個事務關聯的操做具備共同的目標,而且是相互依賴的。若是系統只執行這些操做的一個子集,則可能會破壞事務的整體目標。原子性消除了系統處理操做子集的可能性。  

  · 一致性  
   事務在完成時,必須使全部的數據都保持一致狀態。在相關數據庫中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發人員承擔,他們必須確保應用程序已強制全部已知的完整性約束。例如,當開發用於轉賬的應用程序時,應避免在轉賬過程當中任意移動小數點。  

  · 隔離性  
  由併發事務所做的修改必須與任何其它併發事務所做的修改隔離。事務查看數據時數據所處的狀態,要麼是另外一併發事務修改它以前的狀態,要麼是另外一事務修改它以後的狀態,事務不會查看中間狀態的數據。這稱爲可串行性,由於它可以從新裝載起始數據,而且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將得到最高的隔離級別。在此級別上,從一組可並行執行的事務得到的結果與經過連續運行每一個事務所得到的結果相同。因爲高度隔離會限制可並行執行的事務數,因此一些應用程序下降隔離級別以換取更大的吞吐量。   

  · 持久性  
   事務完成以後,它對於系統的影響是永久性的。該修改即便出現致命的系統故障也將一直保持。  sql

  主要說說這個描述很抽象一致性,舉個例子:A向B轉帳,假設轉帳以前這兩個用戶的錢加起來總共是2000,那麼A向B轉帳以後,無論這兩個帳戶怎麼轉,A用戶的錢和B用戶的錢加起來的總額仍是2000,這個就是事務的一致性。數據庫

 

四種隔離級別數據結構

Read Uncommitted(讀取未提交內容)併發

       在該隔離級別,全部事務均可以看到其餘未提交事務的執行結果。本隔離級別不多用於實際應用,由於它的性能也不比其餘級別好多少。讀取未提交的數據,也被稱之爲髒讀(Dirty Read)。
Read Committed(讀取提交內容)高併發

       這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它知足了隔離的簡單定義:一個事務只能看見已經提交事務所作的改變。這種隔離級別 也支持所謂的不可重複讀(Nonrepeatable Read),由於同一事務的其餘實例在該實例處理其間可能會有新的commit,因此同一select可能返回不一樣結果。
Repeatable Read(可重讀)性能

       這是MySQL的默認事務隔離級別,它確保同一事務的多個實例在併發讀取數據時,會看到一樣的數據行。不過理論上,這會致使另外一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當用戶讀取某一範圍的數據行時,另外一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的「幻影」 行。InnoDB和Falcon存儲引擎經過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。spa

Serializable(可串行化) 
       這是最高的隔離級別,它經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每一個讀的數據行上加上共享鎖。在這個級別,可能致使大量的超時現象和鎖競爭。.net

         這四種隔離級別採起不一樣的鎖類型來實現,若讀取的是同一個數據的話,就容易發生問題。例如:

         髒讀(Drity Read):某個事務已更新一份數據,另外一個事務在此時讀取了同一份數據,因爲某些緣由,前一個RollBack了操做,則後一個事務所讀取的數據就會是不正確的。

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

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

         在MySQL中,實現了這四種隔離級別,分別有可能產生問題以下所示:

 

而後是關於數據庫的各類鎖的總結:

1.共享鎖(又稱讀鎖)、排它鎖(又稱寫鎖):

InnoDB引擎的鎖機制:InnoDB支持事務,支持行鎖和表鎖用的比較多,Myisam不支持事務,只支持表鎖。

 

共享鎖(S):容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。
排他鎖(X):容許得到排他鎖的事務更新數據,阻止其餘事務取得相同數據集的共享讀鎖和排他寫鎖。
意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

 

說明:

 

1)共享鎖和排他鎖都是行鎖,意向鎖都是表鎖,應用中咱們只會使用到共享鎖和排他鎖,意向鎖是mysql內部使用的,不須要用戶干預。

 

2)對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對於普通SELECT語句,InnoDB不會加任何鎖,事務能夠經過如下語句顯示給記錄集加共享鎖或排他鎖。
共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE。

**對於鎖定行記錄後須要進行更新操做的應用,應該使用Select...For update 方式,獲取排它鎖。(用共享鎖,在讀了以後再寫會阻塞,會致使死鎖)

這裏說說Myisam:MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖。

 

3)InnoDB行鎖是經過給索引上的索引項加鎖來實現的,所以InnoDB這種行鎖實現特色意味着:只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然,InnoDB將使用表鎖!

 

2.樂觀鎖、悲觀鎖:

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

1)使用悲觀鎖,咱們必須關閉mysql數據庫的自動提交屬性,採用手動提交事務的方式,由於MySQL默認使用autocommit模式,也就是說,當你執行一個更新操做後,MySQL會馬上將結果進行提交。

2)須要注意的是,在事務中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一筆數據時會等待其它事務結束後才執行,通常SELECT ... 則不受此影響。對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X)。

3)補充:MySQL select…for update的Row Lock與Table Lock

 

使用select…for update會把數據給鎖住,不過咱們須要注意一些鎖的級別,MySQL InnoDB默認Row-Level Lock,因此只有「明確」地指定主鍵(或有索引的地方),MySQL 纔會執行Row lock (只鎖住被選取的數據) ,不然MySQL 將會執行Table Lock (將整個數據表單給鎖住)。

樂觀鎖:

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去作(通常是回滾事務)。那麼咱們如何實現樂觀鎖呢,通常來講有如下2種方式:

 

1).使用數據版本(Version)記錄機制實現,這是樂觀鎖最經常使用的一種實現方式。何謂數據版本?即爲數據增長一個版本標識,通常是經過爲數據庫表增長一個數字類型的 「version」 字段來實現。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,若是數據庫表當前版本號與第一次取出來的version值相等,則予以更新,不然認爲是過時數據。

2).樂觀鎖定的第二種實現方式和第一種差很少,一樣是在須要樂觀鎖控制的table中增長一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version相似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和本身更新前取到的時間戳進行對比,若是一致則OK,不然就是版本衝突。

 

 

總結:兩種鎖各有優缺點,不可認爲一種好於另外一種,像樂觀鎖適用於寫比較少的狀況下,即衝突真的不多發生的時候,這樣能夠省去了鎖的開銷,加大了系統的整個吞吐量。但若是常常產生衝突,上層應用會不斷的進行retry,這樣反卻是下降了性能,因此這種狀況下用悲觀鎖就比較合適。

  另外,高併發狀況下我的認爲樂觀鎖要好於悲觀鎖,由於悲觀鎖的機制使得各個線程等待時間過長,極其影響效率,樂觀鎖能夠在必定程度上提升併發度。

3.表鎖、行鎖

 

表級鎖(table-level locking):MyISAM和MEMORY存儲引擎

行級鎖(row-level locking) :InnoDB存儲引擎

頁面鎖(page-level-locking):BDB存儲引擎

 

表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。

行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。

頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。

相關文章
相關標籤/搜索