資料庫鎖設計的初衷是處理併發問題,作為多使用者共享的資源,資料庫需要合理控制資源的訪問規則,鎖就是用來實現這些訪問規則的資料結構。

根據加鎖的範圍,MySQL裡面的鎖大致可以分成

全域性鎖、表鎖和行鎖

三類。

一、全域性鎖

全域性鎖是對整個資料庫例項加鎖,MySQL提供了加全域性讀鎖的方法,命令是Flush tables with read lock,如果需要整庫處於只讀狀態,可以使用此條命令;以下語句會被阻塞:資料更新語句(增刪改)、資料定義語句(建表、修改表結構等)和更新類事務的提交語句。

全域性鎖使用場景,全庫邏輯備份,使庫處於只讀狀態,但是這樣子備份會導致很大問題(暫停寫入,主從同步延遲)。

真正備份時,需要在可重複讀隔離級別下開啟一個事務。當

mysqldump

使用引數

–single-transaction

時,備份資料之前會啟動一個事務,確保拿到一致性檢視。而由於MVCC的支援,這個過程中的資料是可以正常更新的。

但是

–single-transaction

只適用於

支援事務引擎

的庫表,MyISAM不支援事務操作,只能透過FTWRL方法備份。

A、set global readonly=true

set global readonly=true

方式,可以讓全庫進入只讀狀態,但是仍然推薦FTWRL方式。

1、有些系統中,readonly的值會被用來做其他邏輯,比如判斷是主庫還是備庫。因此,修改global變數的方式影響面更大,不建議使用。

2、異常處理機制上有差異。

如果執行FTWRL命令之後由於客戶端發生異常斷開,MySQL會自動釋放這個全域性鎖,整個庫回到可以正常更新的狀態;

將整個庫設定為readonly之後,如果客戶端發生異常,則資料庫就會一直保持readonly狀態,會導致整個庫長時間處於不可寫狀態。

二、表鎖

A、lock tables

表鎖的語法是

lock tables ... read/write

,與FTWRL類似,可以用

unlock tables

主動釋放鎖,也可以在客戶端斷開的時候自動釋放。

lock tables語法除了會限制別的執行緒的讀寫外,也限定了本執行緒接下來的操作物件。

在還未出現更細粒度的鎖時,表鎖是最常用的併發處理方式。單對於InnoDB一般不使用lock tables命令來控制併發,畢竟鎖住整個表的影響面還是太大。

B、MDL(metadata lock)

MDL不需要顯式使用,在訪問一個表的時候會被自動加上。MDL的作用是,保證讀寫的正確性。

如果一個查詢正在遍歷一個表中的資料,而執行期間另一個執行緒對這個表結構做變更,刪了一列,那麼查詢執行緒拿到的結果跟表結構對不上,肯定是不行的。因此,在MySQL 5。5版本中引入了MDL,當對一個表做增刪改查操作的時候,加MDL讀鎖;當要對錶做結構變更操作的時候,加MDL寫鎖。

讀鎖之間不互斥,因此你可以有多個執行緒同時對一張表增刪改查;

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

雖然MDL鎖是系統預設會加的,但卻是不能忽略的一個機制,經常有人一個小表加個欄位,導致整個庫掛了。給一個表加欄位,或者修改欄位,或者加索引,需要掃描全表的資料。實際上,即使是小表,操作不慎也會出問題。

「MySQL」 - 全域性鎖和表鎖

session A先啟動,對錶t加一個MDL讀鎖,由於session B需要的也是MDL讀鎖,因此可以正常執行;

session C會被blocked,因為session A的MDL讀鎖還沒有釋放,而session C需要MDL寫鎖,因此只能被阻塞;

之後所有要在表t上新申請MDL讀鎖的請求也會被session C阻塞。所有對錶的增刪改查操作都需要先申請MDL讀鎖,就都被鎖住,等於這個表現在完全不可讀寫了。

如果某個表上的查詢語句頻繁,而且客戶端有重試機制,也就是說超時後會再起一個新session再請求的話,這個庫的執行緒很快就會爆滿。

事務中的MDL鎖,在語句執行開始時申請,但是語句結束後並不會馬上釋放,而會等到整個事務提交後再釋放。

參考:

極客時間 -MySQL實戰45講