MySQL 的 Metadata Lock (MDL) 是資料庫並發控制中至關重要卻常被忽視的一環。它雖不直接鎖定資料行,卻能決定 DDL 操作能否順利執行,甚至牽一髮而動全身,導致整個資料庫服務不可用。本文將從原理、版本演進、實際案例、監控診斷及與 Table Cache 的關係等方面,系統梳理 MDL 的核心機制。
MDL 全稱 Metadata Lock,即元數據鎖。它的核心作用是保護表結構定義(Schema)在事務執行期間不被修改。若無 MDL,事務 A 正在讀取表 t,事務 B 卻對表 t 執行 ALTER TABLE 新增欄位,會導致事務 A 看到的表結構前後不一致,甚至返回錯誤結果,破壞事務隔離性。
在 MySQL 5.1 版本中,MDL 僅在語句級別生效,事務提交或回滾即釋放。以下案例展示了這種設計的缺陷:
-- 會話 1 BEGIN; SELECT * FROM t WHERE id=1; -- 返回 id=1, name='python' -- 會話 2 ALTER TABLE t ADD COLUMN comment VARCHAR(200) DEFAULT 'I use Python'; -- 立即執行成功 -- 會話 1 再次查詢 SELECT * FROM t WHERE id=1; -- 返回 Empty set!數據「憑空消失」 ROLLBACK; BEGIN; SELECT * FROM t WHERE id=1; -- 這次卻返回新結構:id=1, name='python', comment='I use Python'
同一事務中,兩次相同查詢返回不同的列結構,嚴重違反了 REPEATABLE READ 隔離級別的承諾。
自 MySQL 5.5 起,MDL 的生命週期延長至事務結束。事務開始時隱式獲取 MDL 共享鎖(讀操作)或排他鎖(寫操作),直到 COMMIT 或 ROLLBACK 才釋放。這意味著:
-- 會話 1:長事務未提交 BEGIN; SELECT * FROM t ORDER BY id; -- 持有 MDL_S 鎖 -- 會話 2:嘗試加欄位,進入 Waiting for table metadata lock ALTER TABLE t ADD COLUMN cc CHAR(10) DEFAULT 'c lang'; -- 會話 3:尋常查詢也被卡住 SELECT * FROM t WHERE id=1; -- 等待 MDL_S,因 DDL 已佔用 MDL_X 等待中
現象:SHOW PROCESSLIST 顯示大量線程處於 "Waiting for table metadata lock" 狀態,DDL 耗時近 1000 秒(見 SHOW PROFILE 中 "rename result table" 階段)。
InnoDB 表若定義了觸發器或外鍵,DML 操作會隱式鎖定相關表的元數據:
-- 建立觸發器:t3 插入時同步寫入 t4 CREATE TRIGGER t3_trigger AFTER INSERT ON t3 FOR EACH ROW BEGIN INSERT INTO t4 SET id = NEW.id; END; -- 會話 1 BEGIN; INSERT INTO t3 VALUES (1); -- 持有 t3、t4 的 MDL -- 會話 2 DROP TRIGGER IF EXISTS t3_trigger; -- 直接 Hang 住
此時 SHOW ENGINE INNODB STATUS 可見事務持有鎖結構,但無行鎖等待,典型的 MDL 爭用。
SHOW VARIABLES LIKE 'lock_wait_timeout'; -- 預設 31536000 秒(1 年),實際上幾乎永不超時
建議根據業務容忍度調整為較小值(如 30~60 秒),避免無限等待佔用連接。
MySQL 5.7+ 可通過 performance_schema.metadata_locks 直接查看;舊版本需組合 INNODB 系統表:
-- 查看被阻塞事務 SELECT * FROM INNODB_TRX WHERE TRX_STATE = 'LOCK WAIT'; -- 關聯鎖信息 SELECT INNODB_LOCKS.* FROM INNODB_LOCKS JOIN INNODB_LOCK_WAITS ON (INNODB_LOCKS.LOCK_TRX_ID = INNODB_LOCK_WAITS.BLOCKING_TRX_ID); -- 定位具體表 SELECT * FROM INNODB_LOCKS WHERE LOCK_TABLE = 'db_name.table_name';
實測發現:DDL 執行後,Open_tables 計數會暫時增加,查詢完成後恢復。這說明 DDL 會強制重新打開表實例,刷新 Table Cache,間接加劇 MDL 爭用開銷。
pt-online-schema-change 或原生 Online DDL(5.6+)降低鎖持有時間;MDL 是 MySQL 保障 Schema 一致性的「隱形守門人」。理解其鎖模式(共享/排他)、生命週期(語句級→事務級)、與 Table Cache 的交互,才能在架構設計與故障排查中游刃有餘。面對「熱表」DDL,務必謹之又謹——一條不當的 ALTER,足以讓整個集群「靜默」數分鐘甚至更久。