鎖是計算機協調多個進程或線程併發訪問某一資源的機制。在數據庫中的數據也是一種供多個用戶使用的共享資源,html
當多個用戶併發地存取數據時,在數據庫加鎖的目的能夠保證數據庫數據的一致性。mysql
在數據庫中,對數據的操做其實只有兩種,即讀和寫,因此可用共享鎖和互斥鎖實現,即共享鎖間之間是兼容的,而互斥鎖間不兼容。sql
- 共享鎖(讀鎖):容許事務讀
- 互斥鎖(寫鎖):容許事務刪除或者更新一行數據
共享鎖表明了讀操做、互斥鎖表明了寫操做,因此咱們能夠在數據庫中並行讀,可是隻能串行寫,只有這樣才能保證不會發生線程競爭,實現線程安全。數據庫
咱們按照鎖的粒度能夠分爲全局鎖、表鎖和行鎖,不一樣的存儲引擎擁有的鎖粒度是不一樣的 編程
在下面的文章中,咱們按照鎖粒度的劃分分別學習全局鎖、表級鎖和行鎖安全
從名稱上就能夠看出,全局鎖是對整個數據庫實例加鎖。MySQL提供了一個加全局讀鎖的方法,即Flush tables with read lock (FTWRL)。當對數據庫使用這個命令時,數據庫將會處於只讀狀態,以後的其它線程的語句:如數據更新語句(數據的增刪改)DML,數據的定義語句(表結構的修改)DDL均會被阻塞。markdown
全局鎖的典型使用場景是作數據庫邏輯備份,也就是將整個庫中的全部表都select出來存成文本。在備份過程當中的整個庫要處於只讀狀態,這會存在如下問題併發
若是選用的引擎不支持事務(如MyISAM),在對數據庫作邏輯備份時只能選擇FTWRL。若是引擎支持事務,而且支持可重複讀的隔離級別(如InnoDB),咱們就能夠用一致性視圖來作備份,MySQL自帶的邏輯備份工具是mysqldump。當mysqldump使用參數-single-transaction時,導入數據以前就能夠啓動一個事務,來確保拿到一致性視圖。在MVCC的支持下,導入數據的過程當中是能夠正常更新的。工具
flush tables with read lock
複製代碼
unlock tables
複製代碼
斷開鏈接也能釋放全局鎖學習
MySQL的表級鎖有兩種:表鎖,元數據鎖(meta data lock MDL),
表鎖的語法是:lock tables ... read/write,能夠利用unlock tables主動釋放鎖,也能夠在鏈接斷開時自動釋放。須要注意的是,lock tables除了限制其它線程的讀寫,也會限制自身接下來的操做對象
舉例
若是在某個線程 A 中執行 lock tables t1 read, t2 write這個語句,則其餘線程寫 t一、讀寫 t2 的語句都會被阻塞。與此同時,線程 A 在執行 unlock tables 以前,也只能執行讀 t一、讀寫 t2 的操做。連寫 t1 都不容許,天然也不能訪問其餘表。
這種操做整個表的影響很大,因此對於使用InnoDB引擎下,通常不適用lock tables命令控制併發。
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鎖的發生呢?
- 避免長事務
- 在DDL操做中設定等待時間
行鎖每次鎖定的是一行數據,行級鎖定不是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,發起死鎖檢測,當發現死鎖後,主動回滾死鎖鏈條中的某一個事務,讓其它事務得以繼續執行。
當咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,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