Mysql鎖的類型與簡析


數據庫鎖設計的初衷是處理並發問題。作為多用戶共享的資源,當出現並發訪問的時候,數據庫需要合理地控制資源的訪問規則。而鎖就是用來實現這些訪問規則的重要數據結構。

根據加鎖的范圍,MySQL 里面的鎖大致可以分成全局鎖、表級鎖和行鎖三類。

 

全局鎖

顧名思義,全局鎖就是對整個數據庫實例加鎖。MySQL 提供了一個加全局讀鎖的方法,命令是 Flush tables with read lock (FTWRL)。當你需要讓整個庫處於只讀狀態的時候,可以使用這個命令,之后其他線程的以下語句會被阻塞:數據更新語句(數據的增刪改)、數據定義語句(包括建表、修改表結構等)和更新類事務的提交語句。

全局鎖的典型使用場景是,做全庫邏輯備份。也就是把整庫每個表都 select 出來存成文本。

官方自帶的邏輯備份工具是 mysqldump。當 mysqldump 使用參數–single-transaction 的時候,導數據之前就會啟動一個事務,來確保拿到一致性視圖。而由於 MVCC 的支持,這個過程中數據是可以正常更新的。

你一定在疑惑,有了這個功能,為什么還需要 FTWRL 呢?一致性讀是好,但前提是引擎要支持這個隔離級別。比如,對於 MyISAM 這種不支持事務的引擎,如果備份過程中有更新,總是只能取到最新的數據,那么就破壞了備份的一致性。這時,我們就需要使用 FTWRL 命令了。

所以,single-transaction 方法只適用於所有的表使用事務引擎的庫。如果有的表使用了不支持事務的引擎,那么備份就只能通過 FTWRL 方法。這往往是 DBA 要求業務開發人員使用 InnoDB 替代 MyISAM 的原因之一。

 

表級鎖

MySQL 里面表級別的鎖有兩種:一種是表鎖,一種是元數據鎖(meta data lock,MDL)。

表鎖

表鎖的語法是 lock tables … read/write。與 FTWRL 類似,可以用 unlock tables 主動釋放鎖,也可以在客戶端斷開的時候自動釋放。需要注意,lock tables 語法除了會限制別的線程的讀寫外,也限定了本線程接下來的操作對象。

在還沒有出現更細粒度的鎖的時候,表鎖是最常用的處理並發的方式。而對於 InnoDB 這種支持行鎖的引擎,一般不使用 lock tables 命令來控制並發,畢竟鎖住整個表的影響面還是太大。

元數據鎖

MDL 不需要顯式使用,在訪問一個表的時候會被自動加上。MDL 的作用是,保證讀寫的正確性。你可以想象一下,如果一個查詢正在遍歷一個表中的數據,而執行期間另一個線程對這個表結構做變更,刪了一列,那么查詢線程拿到的結果跟表結構對不上,肯定是不行的。

因此,在 MySQL 5.5 版本中引入了 MDL,當對一個表做增刪改查操作的時候,加 MDL 讀鎖;當要對表做結構變更操作的時候,加 MDL 寫鎖。

 

  • 讀鎖之間不互斥,因此你可以有多個線程同時對一張表增刪改查。

  • 讀寫鎖之間、寫鎖之間是互斥的,用來保證變更表結構操作的安全性。因此,如果有兩個線程要同時給一個表加字段,其中一個要等另一個執行完才能開始執行。

需要注意的是:MDL 會直到事務提交才釋放,在做表結構變更的時候,你一定要小心不要導致鎖住線上查詢和更新。

 

 

行鎖

MySQL 的行鎖是在引擎層由各個引擎自己實現的。但並不是所有的引擎都支持行鎖,比如 MyISAM 引擎就不支持行鎖。不支持行鎖意味着並發控制只能使用表鎖,對於這種引擎的表,同一張表上任何時刻只能有一個更新在執行,這就會影響到業務並發度。InnoDB 是支持行鎖的,這也是 MyISAM 被 InnoDB 替代的重要原因之一。

在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是不需要了就立刻釋放,而是要等到事務結束時才釋放。這個就是兩階段鎖協議。

知道了這個設定,對我們使用事務有什么幫助呢?那就是,如果你的事務中需要鎖多個行,要把最可能造成鎖沖突、最可能影響並發度的鎖盡量往后放。

舉個例子。

假設你負責實現一個電影票在線交易業務,顧客 A 要在影院 B 購買電影票。我們簡化一點,這個業務需要涉及到以下操作:

  1. 從顧客 A 賬戶余額中扣除電影票價;

  2. 給影院 B 的賬戶余額增加這張電影票價;

  3. 記錄一條交易日志。

也就是說,要完成這個交易,我們需要 update 兩條記錄,並 insert 一條記錄。當然,為了保證交易的原子性,我們要把這三個操作放在一個事務中。那么,你會怎樣安排這三個語句在事務中的順序呢?

試想如果同時有另外一個顧客 C 要在影院 B 買票,那么這兩個事務沖突的部分就是語句 2 了。因為它們要更新同一個影院賬戶的余額,需要修改同一行數據。

根據兩階段鎖協議,不論你怎樣安排語句順序,所有的操作需要的行鎖都是在事務提交的時候才釋放的。所以,如果你把語句 2 安排在最后,比如按照 3、1、2 這樣的順序,那么影院賬戶余額這一行的鎖時間就最少。這就最大程度地減少了事務之間的鎖等待,提升了並發度。

死鎖和死鎖檢測

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

這時候,事務 A 在等待事務 B 釋放 id=2 的行鎖,而事務 B 在等待事務 A 釋放 id=1 的行鎖。 事務 A 和事務 B 在互相等待對方的資源釋放,就是進入了死鎖狀態。當出現死鎖以后,有兩種策略:

  • 一種策略是,直接進入等待,直到超時。這個超時時間可以通過參數 innodb_lock_wait_timeout 來設置。
  • 另一種策略是,發起死鎖檢測,發現死鎖后,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以繼續執行。將參數 innodb_deadlock_detect 設置為 on,表示開啟這個邏輯。

減少死鎖的主要方向,就是控制訪問相同資源的並發事務量。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM