1. 程式人生 > 實用技巧 >MySQL-全句鎖、表鎖和元資料鎖

MySQL-全句鎖、表鎖和元資料鎖

全域性鎖

全域性鎖是鎖住整個資料庫例項,只能讀,任何關於更新操作的語句都會阻塞。

全域性鎖的適用場景

針對資料庫做全庫的邏輯備份操作時,需要使用全域性鎖。

全域性鎖的影響:

  • 如果在主庫上做全域性鎖操作,業務基本停擺
  • 如果在從庫上做全域性鎖操作,備份期間從庫不能更新主庫同步過來的binlog,可能導致主從不一致

如果不加鎖,備份完成後可能得到不一致的狀態,不安全,所以一定要加鎖。

如何加全域性鎖?
  1. 非innodb引擎,需要使用Flush table with read lock命令
  2. innodb引擎,可以使用mysqldump命令實現,加入一個引數 --single-transaction,在備份前開啟一個事務,保證檢視的一致性。
  3. (不建議使用)set global readonly=true; 原因如下:
    • 修改引數的影響面大。有些系統中,這個引數用來作其他用途,比如判斷是主庫還是從庫,因此修改這個引數的影響面比較大。
    • 異常的處理機制不友好。FTWRL如果客戶端連線異常斷開,mysql會自動釋放全域性鎖;如果設定引數,出現異常後,資料庫仍舊是readonly為true的狀態,風險較高。

表鎖

表鎖是鎖住整張表,通過不同的表鎖設定,控制併發訪問。某些引擎不支援行鎖,需要通過表鎖來控制併發。支援行鎖的引擎,就不建議使用表鎖了。

如何加表鎖?

lock tables t1 read,t2 write;
這個語句有兩個含義:

  • 對其他執行緒來說,t1表,可以讀,不可以寫;t2表,讀寫都不可以
  • 對本執行緒來說,t1表只能讀,t2表只能讀寫

元資料鎖(Metadata Lock 簡稱MDL)

元資料鎖主要是面向DML和DDL之間的併發控制,如果對一張表做DML增刪改查操作的同時,有一個執行緒在做DDL操作,不加控制的話,就會出現錯誤和異常。元資料鎖不需要我們顯式的加,系統預設會加。

元資料鎖的原理

當做DML操作時,會申請一個MDL讀鎖
當做DDL操作時,會申請一個MDL寫鎖
讀鎖之間不互斥,讀寫和寫寫之間都互斥。

實驗驗證
mysql實驗環境:5.7
mysql客戶端:mysql命令列工具

一共開啟3個session,SessionA,SessionB,SessionC。

第一次實驗:
時間線和執行命令如下

A:begin; select * from t;-------------------------------------------------commit;------------
----------------------------B: alter table t add f1 int;-----------------------------------------
--------------------------------------------------------C: select * from t;----------------------

實驗結果:
在執行commit前,B和C都會阻塞住。
執行commit後,看起來B先返回資料,C後返回資料。

第二次實驗:
時間線和執行命令如下

A:begin; select * from t;---------------------------------------------------------commit;----
----------------------------B: alter table t add f2 int;---------------------------------------commit--
----------------------------------------------------------C: begin; select * from t;-------------

實驗結果:
在執行commit前,B和C都會阻塞住。
執行commit後,B正常返回,C依舊阻塞住。

在B執行commit後,C正常返回。

元資料實驗結果分析

現象1
當開啟一個事務時,在事務中做DML操作時,就會拿到讀鎖,在事務未提交之前,如果有一個DDL操作,那麼會阻塞,同時還會阻塞後面的所有讀和寫操作。

原因
獲取鎖有一個佇列,寫操作先進入佇列中,並且寫操作的優先順序很高,如果寫操作被阻塞了,後面的讀和寫都會被阻塞。

現象2
在讀和寫都被阻塞後,提交事務,看起來反倒是讀先拿到鎖,返回資料。

原因
mysql5.6以後,加入了onlineDDL的操作,一共有5個步驟。

  1. 申請MDL寫鎖
  2. 申請到後降級為讀鎖
  3. 真正的DDL操作
  4. 申請MDL寫鎖
  5. 釋放鎖

在SessionA的事務提交後,確實是SessionB寫操作先拿到寫鎖,然後在第二步降級為讀鎖後,後面的SessionC的讀操作就可以正常獲取讀鎖,執行後返回。

  • 如果SessionC釋放了讀鎖,SessionB的寫操作在第四步的時候就可以成功
  • 如果SessionC沒釋放讀鎖,SessionB的寫操作在第四步就會阻塞住

所以SessionC如果是自動提交,執行完畢後自動釋放鎖,SessionB也可以返回;SessionC如果使用begin手動開啟事務,執行完成後,commit前都不會釋放鎖,SessionB也就會一直阻塞,直到SessionC執行了commit操作SessionB才會返回。