全局鎖
全局鎖是鎖住整個數據庫實例,只能讀,任何關於更新操作的語句都會阻塞。
全局鎖的適用場景
針對數據庫做全庫的邏輯備份操作時,需要使用全局鎖。
全局鎖的影響:
- 如果在主庫上做全局鎖操作,業務基本停擺
- 如果在從庫上做全局鎖操作,備份期間從庫不能更新主庫同步過來的binlog,可能導致主從不一致
如果不加鎖,備份完成后可能得到不一致的狀態,不安全,所以一定要加鎖。
如何加全局鎖?
- 非innodb引擎,需要使用Flush table with read lock命令
- innodb引擎,可以使用mysqldump命令實現,加入一個參數 --single-transaction,在備份前開啟一個事務,保證視圖的一致性。
- (不建議使用)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;-----------------------------------------------
----------------------------------------------------------C: begin; select * from t;------------commit-
實驗結果:
在執行commit前,B和C都會阻塞住。
執行commit后,B正常返回,C依舊阻塞住。
在B執行commit后,C正常返回。
元數據實驗結果分析
現象1
當開啟一個事務時,在事務中做DML操作時,就會拿到讀鎖,在事務未提交之前,如果有一個DDL操作,那么會阻塞,同時還會阻塞后面的所有讀和寫操作。
原因
獲取鎖有一個隊列,寫操作先進入隊列中,並且寫操作的優先級很高,如果寫操作被阻塞了,后面的讀和寫都會被阻塞。
現象2
在讀和寫都被阻塞后,提交事務,看起來反倒是讀先拿到鎖,返回數據。
原因
mysql5.6以后,加入了onlineDDL的操作,一共有5個步驟。
- 申請MDL寫鎖
- 申請到后降級為讀鎖
- 真正的DDL操作
- 申請MDL寫鎖
- 釋放鎖
在SessionA的事務提交后,確實是SessionB寫操作先拿到寫鎖,然后在第二步降級為讀鎖后,后面的SessionC的讀操作就可以正常獲取讀鎖,執行后返回。
- 如果SessionC釋放了讀鎖,SessionB的寫操作在第四步的時候就可以成功
- 如果SessionC沒釋放讀鎖,SessionB的寫操作在第四步就會阻塞住
所以SessionC如果是自動提交,執行完畢后自動釋放鎖,SessionB也可以返回;SessionC如果使用begin手動開啟事務,執行完成后,commit前都不會釋放鎖,SessionB也就會一直阻塞,直到SessionC執行了commit操作SessionB才會返回。