MySQL鎖,你真的理解麼?

MySQL鎖

1、基本概念

鎖是計算機協調多個進程或線程併發訪問某一資源的機制。在數據庫中的數據也是一種供多個用戶使用的共享資源,

當多個用戶併發地存取數據時,在數據庫加鎖的目的可以保證數據庫數據的一致性。

  • 鎖的類型

在數據庫中,對數據的操作其實只有兩種,即讀和寫,所以可用共享鎖和互斥鎖實現,即共享鎖間之間是兼容的,而互斥鎖間不兼容。

  • 共享鎖(讀鎖):允許事務讀
  • 互斥鎖(寫鎖):允許事務刪除或者更新一行數據

共享鎖代表了讀操作、互斥鎖代表了寫操作,所以我們可以在數據庫中並行讀,但是隻能串行寫,只有這樣才能保證不會發生線程競爭,實現線程安全。

  • 鎖的粒度

我們按照鎖的粒度可以分爲全局鎖、表鎖和行鎖,不同的存儲引擎擁有的鎖粒度是不同的

在下面的文章中,我們按照鎖粒度的劃分分別學習全局鎖、表級鎖和行鎖

2、全局鎖

2.1基本概念

從名稱上就可以看出,全局鎖是對整個數據庫實例加鎖。MySQL提供了一個加全局讀鎖的方法,即Flush tables with read lock (FTWRL)。當對數據庫使用這個命令時,數據庫將會處於只讀狀態,之後的其它線程的語句:如數據更新語句(數據的增刪改)DML,數據的定義語句(表結構的修改)DDL均會被阻塞。

2.2使用場景

全局鎖的典型使用場景是做數據庫邏輯備份,也就是將整個庫中的所有表都select出來存成文本。在備份過程中的整個庫要處於只讀狀態,這會存在以下問題

  • 如果你在主庫上備份,那麼在備份期間都不能執行更新,業務基本上就能停止
  • 如果你在從庫上備份,那麼備份期間從庫不能執行主庫同步過來的binlog,會導致主從延遲。

如果選用的引擎不支持事務(如MyISAM),在對數據庫做邏輯備份時只能選擇FTWRL。如果引擎支持事務,並且支持可重複讀的隔離級別(如InnoDB),我們就可以用一致性視圖來做備份,MySQL自帶的邏輯備份工具是mysqldump。當mysqldump使用參數-single-transaction時,導入數據之前就可以啓動一個事務,來確保拿到一致性視圖。在MVCC的支持下,導入數據的過程中是可以正常更新的。

2.3lock/unlock命令

  • 全局鎖加鎖命令
flush tables with read lock
  • 釋放全局鎖的命令
unlock tables

斷開連接也能釋放全局鎖

3、表級鎖

MySQL的表級鎖有兩種:表鎖,元數據鎖(meta data lock MDL),

3.1表鎖

表鎖的語法是:lock tables ... read/write,可以利用unlock tables主動釋放鎖,也可以在連接斷開時自動釋放。需要注意的是,lock tables除了限制其它線程的讀寫,也會限制自身接下來的操作對象

舉例

如果在某個線程 A 中執行 lock tables t1 read, t2 write這個語句,則其他線程寫 t1、讀寫 t2 的語句都會被阻塞。與此同時,線程 A 在執行 unlock tables 之前,也只能執行讀 t1、讀寫 t2 的操作。連寫 t1 都不允許,自然也不能訪問其他表。

這種操作整個表的影響很大,所以對於使用InnoDB引擎下,一般不適用lock tables命令控制併發。

3.2MDL

MDL全稱是metadata lock,即元數據鎖,它的作用主要是爲了維護表中數據的一致性,即用於解決DDL操作與DML操作的一致性。

  • 解決的問題

其主要解決了2個問題,一個是事務隔離問題,比如在可重複隔離級別下,會話A在2次查詢期間,會話B對錶結構做了修改,兩次查詢結果就會不一致,無法滿足可重複讀的要求;另外一個是數據複製的問題,比如會話A執行了多條更新語句期間,另外一個會話B做了表結構變更並且先提交,就會導致slave在重做時,先重做alter,再重做update時就會出現複製錯誤的現象。

元數據鎖是server層的鎖,表級鎖,每執行一條DML、DDL語句時都會申請MDL鎖,DML操作需要MDL讀鎖,DDL操作需要MDL寫鎖(MDL加鎖過程是系統自動控制,無法直接干預,讀讀共享,讀寫互斥,寫寫互斥),申請MDL鎖的操作會形成一個隊列,隊列中寫鎖獲取優先級高於讀鎖。

NOTE:一旦出現寫鎖等待,不但當前操作會被阻塞,同時還會阻塞後續該表的所有操作(讀操作當然也會被阻塞)。事務一旦申請到MDL鎖後,直到事務執行完纔會將鎖釋放。

MDL鎖一旦發生可能會對數據庫的業務帶來其它的影響,因爲後續對該表的訪問都會被阻塞,造成連接積壓。那麼我們該如何避免MDL鎖的發生呢?

  • MDL鎖的優化
  • 避免長事務
  • 在DDL操作中設定等待時間

4、行鎖

行鎖每次鎖定的是一行數據,行級鎖定不是MySQL自己實現鎖定的方式,是由存儲引擎實現的(InnoDB)自己實現的。

InnoDB實現了以下兩種類型的行鎖

  • 共享鎖(讀鎖)

允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。若事務T對數據對象A加上共享鎖,則事務T可以讀A但不能修改A,而其他事務只能再對對象A加共享鎖,而不能加排他鎖,直到事務T釋放A上的共享鎖。這保證了其他事務可以讀A,但在事務T釋放A上的S鎖之前不能對A做任何修改。

  • 排他鎖(寫鎖)

允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上排他鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖

NOTE:排他鎖指的是一個事務在一行數據加上排他鎖後,其他事務不能再在其上加其他的鎖。mysql InnoDB引擎默認的修改數據語句:update,delete,insert都會自動給涉及到的數據加上排他鎖,select語句默認不會加任何鎖類型,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句。所以加過排他鎖的數據行在其他事務種是不能修改數據的,也不能通過for update和lock in share mode鎖的方式查詢數據,但可以直接通過select …from…查詢數據,因爲普通查詢沒有任何鎖機制。

爲了使得表鎖和行鎖共存,實現多粒度鎖機制,InnoDB存在了兩種內部使用的的意向鎖

  • 意向共享鎖

事務打算給數據行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的意向共享鎖。

  • 意向排他鎖

事務打算給數據行加排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的意向排他鎖

NOTE:意向鎖是InnoDB自動加的,不需用戶干預。對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖;對於普通SELECT語句,InnoDB不會加任何鎖。

  • 死鎖

當併發系統中不同線程出現循環資源依賴,涉及的線程都在等待別的線程釋放資源時,就會導致這幾個線程都進入無限等待的狀態,稱爲死鎖

案例

事務A 事務B
begin;update t set k=k+1 where id=1; begin
update t set k=k+1 where id=2;
update t set k=k+1 where id=2;
update t set k=k+1 where id=1;

事務 A 在等待事務 B 釋放 id=2 的行鎖,而事務 B 在等待事務 A 釋放 id=1 的行鎖。 事務 A 和事務 B 在互相等待對方的資源釋放,就是進入了死鎖狀態。

解除死鎖的策略

  • 設置超時時間參數innodb_lock_wait_timeout,
  • 將參數 innodb_deadlock_detect 設置爲 on,發起死鎖檢測,當發現死鎖後,主動回滾死鎖鏈條中的某一個事務,讓其它事務得以繼續執行。
  • 間隙鎖(Next-key)

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

使用間隙鎖的目的是防止幻讀

關注公衆號:10分鐘編程,讓我們每天博學一點點

公衆號回覆success,領取獨家整理的學習資源,JAVA、大數據全套視頻資料

參考

[1]林曉斌.《MySQL實戰45講》

[2]https://www.cnblogs.com/kunjian/p/11993708.html

[3]http://mysqlpub.com/thread-5383-1-1.html

[4]http://blog.csdn.net/c466254931/article/details/53463596

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章