1. 程式人生 > 實用技巧 >Mysql的隔離級別以及產生的問題

Mysql的隔離級別以及產生的問題

併發導致的事務問題

更新丟失:和別的事務讀到相同的資料,各自對資料進行操作,自己寫的被覆蓋了
比如:事務A和事務B同時對同一資料操作,由於事務A先提交,事務A做的改變被事務B覆蓋了

髒讀:一個事務讀取了另一個事務還沒有提交的資料
比如:事務A讀取了事務B更新但未提交的資料後,事務B進行了回滾操作,那麼事務A讀取到的是髒資料。

不可重複讀:同一個事務中,多次讀出的同一條資料是不一樣的。
比如:事務A多次讀取同一資料,事務B在事務A多次讀取的過程中,對該資料進行了更新和提交,導致事務A多次讀取的資料不一樣。

幻讀:同一事務中,按照同一條件多次讀取出的資料量不一樣。
比如:事務A按照相同條件多次讀取資料,事務B在事務A多次讀取的過程中,對資料進行了增或刪,導致事務A多次讀取的資料量不一樣。

SQL標準定義了4種隔離級別。隔離級別 低的一般支援更高的併發處理,並擁有更低的系統開銷。

  • Read Uncommitted (讀取未提交內容): 所有事務都可以讀取到其他未提交事務的執行結果。該隔離級別一般不用
    原理:①事務對當前被讀取的資料不加鎖;②事務在更新某資料的瞬間(發生更新的瞬間),必須先加行級共享鎖,直到事務結束才釋放。
  • Read Commited(讀取提交內容): 一個事務只能讀取其他已提交事務的執行結果。這是大多數資料庫系統的預設隔離級別。
    原理:①事務對當前被讀取的資料加行級共享鎖(當讀到時才加鎖),一旦讀完,立即釋放該行級共享鎖,所以會出現不可重讀的問題;②事務在更新某資料的瞬間(發生更新的瞬間),必須先對其加行級排他鎖
    ,直到事務結束才釋放。所以不會出現髒讀的問題。
  • Repeatable Read(可重讀) 它確保同一事務的多個例項在併發讀取資料時,能看到同樣的資料。這是Mysql的預設隔離級別。但是這個級別會導致幻讀,因為沒有隔離insert語句。
    原理:①事務在讀取資料的瞬間(開始讀取的瞬間),必須先對其加行共享鎖,直到事務結束才釋放;②事務在更新某資料的瞬間(發生更新的瞬間),必須先對其加行級排他鎖,直到事務結束才釋放

mysql不會出現幻讀。mysql的實現和標準定義的Repeatable Read隔離級別有差別。
參考https://my.oschina.net/xinxingegeya/blog/296513

MVCC只在RC和RR兩個隔離級別下工作

  • Serializable(可序列化) 隔離級別最高,它通過強制事務排序,避免了衝突,從而解決幻讀的問題。該級別會給每個讀的資料行加上共享鎖,但可能會導致大量的超時現象和鎖競爭。
    原理:①事務在讀取資料時,必須先對其加表級共享鎖,直到事務結束才釋放;②事務在更新資料時,必須先對其加表級排他鎖,直到事務結束才釋放
    每個隔離級別可能產生的問題:
    在這裡插入圖片描述

InnoDB的鎖

在這裡插入圖片描述

共享鎖
S鎖,又稱讀鎖。如果事務T對資料A加上S鎖,則事務T可以讀A但是不能修改A;其他事務只能對資料A加上S鎖,不能加X鎖,直到T釋放S鎖。實現了允許多個執行緒同時獲取一個鎖,一個鎖可以同時被多個執行緒擁有

排他鎖
X鎖,又稱寫鎖。如果事務T對資料A加上X鎖,則事務T可以讀寫A,其他事務不能對A加任何鎖,直到T釋放S鎖。排他鎖也成獨佔鎖,一個鎖在某一時刻只能被一個執行緒佔有。

意向共享鎖
(InnoDB)IS鎖,事務打算給資料行加行共享鎖,事務在給一個數據加行共享鎖前必須先取得該表的IS鎖

意向排他鎖
(InnoDB)IX鎖,事務打算給資料行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

共享鎖和排他鎖都是行鎖(Record Locks),意向鎖都是表鎖。

行鎖,是加在索引行(不是資料行)上的鎖。比如select * from user where id=1 and id=10 for update,就會在id=1和id=10的索引行上加Record Lock。
行鎖都是基於索引的,如果SQL語句沒有用到索引,使用的是表鎖。

意向鎖的作用

主要用於多粒度鎖並存的情況。

比如事務A要在一個表上加S鎖,如果表中的一行已被事務B加了X鎖,那麼該鎖的申請也應被阻塞。如果表中的資料很多,逐行檢查鎖標誌的開銷將很大,系統的效能將會受到影響。為了解決這個問題,可以在表級上引入新的鎖型別來表示其所屬行的加鎖情況,這就引出了“意向鎖”的概念。

舉個例子,如果表中有一億條記錄,事務A把其中幾條記錄上了行鎖,這時事務B需要給這個表加表級鎖,如果沒有意向鎖的話,那B就要去表中查詢這一億條記錄是否上鎖了。如果存在意向鎖,那麼假如事務A在更新一條記錄之前,先加意向鎖,再加X鎖,事務B先檢查該表上是否存在意向鎖,存在的意向鎖是否與自己準備加的鎖衝突,如果有衝突,則等待直到事務A釋放,而無須逐條記錄去檢測。事務B更新表時,無須知道到底哪一行被鎖了,它只需要知道被鎖了。

記錄鎖

Record Locks,根據索引鎖住表中的某條記錄。

作用:避免資料在查詢的時候被修改的重複讀問題,也避免了在修改的事務未提交前被其他事務讀取的髒讀問題。

間隙鎖
(RR隔離級別)Gap Locks,他會鎖住兩個索引之間的區域,可以解決幻讀的問題。比如select * from user where id>1 and id<10 for update,就會在id為(1,10)的索引區間上加Gap Lock。

Next-Key Locks

也叫臨鍵鎖,他是Record Lock + Gap Lock形成的一個閉區間鎖。比如select * from user where id>=1 and id<=10 for update,就會在id為[1,10]的索引閉區間上加Next-Key Lock

作用:結合記錄鎖和間隙鎖的特性,臨鍵鎖避免了在範圍查詢時出現髒讀、重複讀、幻讀問題。加了臨鍵鎖之後,在範圍區間內資料不允許被修改和插入。

記錄鎖、間隙鎖、臨鍵鎖都是排他鎖

什麼時候會加鎖

  • insert、delete和update 加排他鎖
  • select … for update 加排他鎖
  • select … lock in share mode 加共享鎖

樂觀鎖和悲觀鎖(兩種機制)

悲觀鎖與樂觀鎖都是對被修改資料在併發情況下的一種保護機制;這裡悲觀與樂觀的含義是指對於即將修改的資料被外界修改持一種悲或樂的態度;即:悲觀鎖是指我認為當我要修改一個數據的時候,別人也在修改,所以我要對即將操作的資料進行全程加鎖,以保證我的操作不會被別人所影響;樂觀鎖是指當我要修改資料的時候,別人一般不會再修改,因此,我只在提交我的修改時再加鎖,而不用全程加鎖。由此可見:悲觀鎖的加鎖時間更長。

樂觀鎖常用的方法

  • 使用資料版本(Version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。何謂資料版本?即為資料增加一個版本標識,一般是通過為資料庫表增加一個數字型別的 “version” 欄位來實現。當讀取資料時,將version欄位的值一同讀出,資料每更新一次,對此version值加一。當我們提交更新的時候,判斷資料庫表對應記錄的當前版本資訊與第一次取出來的version值進行比對,如果資料庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期資料。
  • 樂觀鎖定的第二種實現方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個欄位,名稱無所謂,欄位型別使用時間戳(timestamp), 和上面的version類似,也是在更新提交的時候檢查當前資料庫中資料的時間戳和自己更新前取到的時間戳進行對比,如果一致則OK,否則就是版本衝突

二者比較

使用悲觀鎖需要關閉mysql的自動提交屬性

兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下,即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生衝突,上層應用會不斷的進行retry,這樣反倒是降低了效能,所以這種情況下用悲觀鎖就比較合適。

高併發情況下樂觀鎖要好於悲觀鎖,因為悲觀鎖的機制使得各個執行緒等待時間過長,極其影響效率,樂觀鎖可以在一定程度上提高併發度。

MVCC(多版本併發控制)

InnoDB下。

隔離級別在可重複讀和讀已提交的情況下,MVCC實現了在讀資料的時候不需要加鎖操作,提高了資料庫的併發能力。在寫資料的時候,還是需要用到鎖的。

讀已提交

每次select時都會通過MVCC獲取當前資料的最新快照,不加任何鎖。

版本生成時機:每次select時。這就意味著,如果我們在事務A中執行多次的select,在每次select之間有其他事務更新了我們讀取的資料並提交了,那就出現了不可重複讀

鎖的範圍: 因為沒有間隙鎖,這就意味著,如果我們在事務A中多次執行select * from user where age>18 and age<30 for update時,其他事務是可以往age為(18,30)這個區間插入/刪除資料的,那就出現了幻讀

可重讀

MVCC版本的生成時間: 一次事務中只在第一次select時生成版本,後續的查詢都是在這個版本上進行,從而實現了可重複讀

鎖的範圍: 在行鎖的基礎上,加上Gap Lock,從而形成Next-Key Lock,在所有遍歷過的(不管是否匹配條件)索引行上以及之間的區域上,都加上鎖,阻塞其他事務在遍歷範圍內進行寫操作,從而避免了幻讀

參考:資料庫原理-事務隔離與多版本併發控制(MVCC)
資料庫的四種隔離級別
資料庫隔離級別 及 其實現原理
MySQL的四種事務隔離級別以及各種鎖的問題
一文看懂 MySQL事務隔離級別與鎖
[轉載]資料庫MVCC 隔離級別
MySQL InnoDB鎖機制全面解析分享
再談mysql鎖機制及原理—鎖的詮釋