Mysql研磨之InnoDB行鎖模式

動物園裡的一隻程式猿發表於2018-05-08

事務併發帶來的一些問題

(1)更新丟失(LostUpdate):當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題最後的更新覆蓋了由其他事務所做的更新

(2)髒讀(Dirty Reads):一個事務正在對一條記錄做修改,在這個事務完成並且提交前,這條記錄的資料就處於不一致狀態;這時,另一個事務也來讀取這一條記錄,如果不加控制,第二個事務讀取了這些髒資料,並據此做進一步的處理,就會產生未提交的資料依賴關係,這種形象的叫做髒讀

(3)不可重複讀(Non-Respeatable Reads):一個事務在讀取某些資料後的某個時間,再次獨缺以前讀過的資料,卻發現其讀出的資料已經發生了改變或某些記錄已經被刪除了,這種現象就叫做不可重複讀

(4)幻讀(Phantom Reads):一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料,這種現象就稱為幻讀

  

一、InnoDB的行鎖模式和加鎖方法

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

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

頁面鎖:開銷和枷鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖粒度界於表鎖和行鎖之間,併發度一般

InnoDB支援表鎖和行鎖,預設情況下是採用行級鎖

InnoDB實現了兩種型別的行鎖

  1、共享鎖(S):允許一個事物去讀一行,阻止其他事物獲得相同資料集的排他鎖

  2、排他鎖(X):允許獲得排他鎖的事物更新資料,阻止其他事物取得相同資料集的共享讀鎖和排他寫鎖

換一種說法

  共享鎖【S鎖】
    又稱讀鎖,若事務T對資料物件A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。

  排他鎖【X鎖】
    又稱寫鎖。若事務T對資料物件A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。這保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A

  為了允許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。

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

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

如果一個事物請求的鎖模式和當前鎖相容,則InnoDB就將請求的鎖授予該事物,反之如果兩者不相容,該事物就要等待鎖釋放。

  意向鎖是InnoDB自動加的,不需使用者干預。對update、delete、insert語句,innoDB會自動給涉及資料集加排他鎖;對於普通的select語句,InnoDB不會加任何鎖;但是事物可以通過以下語句顯示給記錄集加共享鎖和排他鎖 

  顯示共享鎖 

select * from brand where brand_code = '00AG' LOCK IN SHARE MODE

   顯示排他鎖

select * from brand where brand_code = '00AG' FOR UPDATE

 

二、InnoDB行鎖實現方式

  Innodb行鎖是通過給索引上的索引項加鎖實現的,如果沒有索引,InnoDB將通過隱藏的聚簇索引來對記錄加鎖

  InnoDB行鎖分為三種方式:

  Record lock:對索引項加鎖

  Gap lock:對索引項之間的間隙、第一條記錄前的間隙或最後一條記錄後的間隙加鎖

  Next-key lock:前兩種的組合,對記錄及前面的間隙加鎖

InnoDB行鎖實現特點意味著:如果不通過索引條件檢索資料,那麼InnoDB將對錶中的所有記錄加鎖,實際效果根表鎖一樣

  1、在不通過索引條件查詢時,InnoDB會鎖定表中所有記錄

  2、由於Mysql行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現鎖衝突的

  3、當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對資料加鎖

  4、有時候雖然在條件中使用了索引欄位,但是是否使用索引來檢查資料是由Mysq通過判斷不同的執行計劃來決定的,如果Mysql認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引。這個時候Mysql將會使用表鎖,不會使用行鎖。所以在分析鎖衝突的時候不要遺漏了用EXPLAIN觀察Mysql執行計劃,是否走了索引

  

三、Next-key鎖

  當我們用範圍條件而不是相等條件檢索資料,並請求共享鎖或排他鎖時候,InnoDB會給符合條件的已有資料的索引項加鎖;對於鍵值在條件範圍內但是並不存在的記錄,叫做間隙(GAP),InnoDB也會對間隙加鎖,這種機制就是Next-Key。除了對範圍條件加鎖時候加鎖使用Next-key鎖,對不存在的記錄加鎖,也會使用Next-key鎖

select * from brand where id>100 FOR UPDATE

  InnoDB對間隙加鎖的目的,是為了防止幻讀。比如說針對於上面的SQL,如果這個時候別的事務插入了大於100的任何記錄,那麼本次事務如果再次執行上述語句,就會發生幻讀。

  InnoDB這種機制會阻塞符合條件範圍內鍵值的併發插入,這往往會造成嚴重的鎖等待。

 

 四、InnoDB表鎖

  在InnoDB表,絕大多數情況都應該使用行級鎖,因為事務和行鎖往往是我們選擇InnoDB表的理由。但是在某些情況下也需要使用表級鎖

  1、事務需要更新大部分或全部資料,表又比較大,如果使用預設的行鎖,不僅這個事務執行效率低,而且可能造成其他事務長時間鎖等待和鎖衝突,這種情況下考慮使用表鎖來提高該事務的執行速度

  2、事務設計多個表,比較複雜,很可能引起死鎖,造成大量事務回滾。這種情況可以考慮一次性鎖定事務設計的表,從而避免死鎖,減少資料庫因事務回滾帶來的開銷

  

死鎖:

  1、在應用中,如果不同的程式會併發存取多個表,應儘量約定以相同的順序來訪問表,這樣可以大大降低產生死鎖的機會

  2、在程式以批量方式處理資料的時候,如果事先對資料排序,保證對每個執行緒按固定的順序來處理記錄,也可以大大降低出現死鎖的問題

  3、在事務中,如果要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,而不應先申請共享鎖,更新時在申請排他鎖,因為當用於申請排他鎖時,其他事務可能又已經獲得了相同記錄的共享鎖,從而造成鎖衝突,甚至死鎖

  4、

 

相關文章