MDL全稱為metadata lock,即元數據鎖。MDL鎖主要作用是維護表元數據的數據一致性,在表上有活動事務(顯式或隱式)的時候,不可以對元數據進行寫入操作。因此從MySQL5.5版本開始引入了MDL鎖,來保護表的元數據信息,用于解決或者保證DDL操作與DML操作之間的一致性。
模擬和定位MDL鎖
表結構
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
會話模擬
session A
session B
lock table t write
select * from t where id =1
session A 通過 lock table 命令持有表 t 的 MDL 寫鎖,而 session B 的查詢需要獲取 MDL 讀鎖。所以,session B 進入等待狀態。
mysql> select * from processlist;
+------+--------+-----------+--------+---------+------+---------------------------------+---------------------------+
| ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO |
+------+--------+-----------+--------+---------+------+---------------------------------+---------------------------+
| 3582 | tenmao | localhost | tenmao | Sleep | 115 | | NULL |
| 3583 | tenmao | localhost | tenmao | Query | 97 | Waiting for table metadata lock | select * from t where t=1 |
+------+--------+-----------+--------+---------+------+---------------------------------+---------------------------+
沖突解決
這類問題的處理方式,就是找到誰持有 MDL 寫鎖,然后把它 kill 掉。但是,由于在 show processlist 的結果里面,session A 的 Command 列是“Sleep”,導致查找起來很不方便。不過有了 performance_schema 和 sys 系統庫以后,就方便多了。(MySQL 啟動時需要設置 performance_schema=on,相比于設置為 off 會有 10% 左右的性能損失)通過查詢 sys.schema_table_lock_waits 這張表,我們就可以直接找出造成阻塞的 process id,把這個連接用 kill 命令斷開即可。
mysql> SELECT blocking_pid FROM sys.schema_table_lock_waits;
+--------------+
| blocking_pid |
+--------------+
| 3582 |
+--------------+
如何優化與避免MDL鎖
MDL鎖一旦發生會對業務造成極大影響,因為后續所有對該表的訪問都會被阻塞,造成連接積壓。我們日常要盡量避免MDL鎖的發生,下面給出幾點優化建議可供參考:
開啟metadata_locks表記錄MDL鎖。
設置參數lockwaittimeout為較小值,使被阻塞端主動停止。
規范使用事務,及時提交事務,避免使用大事務。
增強監控告警,及時發現MDL鎖。
DDL操作及備份操作放在業務低峰期執行。
少用工具開啟事務進行查詢,圖形化工具要及時關閉。
參考