MDL(Metadata Lock,元數據鎖) 是 MySQL 5.5+ 引入的鎖子系統,專門保護數據庫對象定義(表結構、視圖、觸發器等)不被並發 DDL 破壞,同時最大化 DML 並發度。本文從設計目標、鎖類型、兼容矩陣、核心數據結構、加鎖/等待/喚醒流程五個維度,系統梳理 MDL 實現邏輯(基於 sql/mdl.cc)。
SELECT * FROM t1 時,TX2 DROP TABLE t1 必須等待 TX1 提交/回滾,否則 TX1 可能讀到已刪除的表結構,導致 Crash 或 Binlog 邏輯錯誤。MySQL 在傳統 S/X、IS/IX 基礎上,按「對字典操作」與「對數據操作」二維度細分:
| 鎖類型 | 語義 | 典型語句 |
|---|---|---|
| MDL_INTENTION_EXCLUSIVE (IX) | 意向排他,僅用於範圍鎖 | — |
| MDL_SHARED | 純訪問字典,不碰數據 | SHOW TABLES |
| MDL_SHARED_HIGH_PRIO | 高優先級共享,僅字典 | DESC TABLE |
| MDL_SHARED_READ (SR) | 共享讀,讀數據 | SELECT |
| MDL_SHARED_WRITE (SW) | 共享寫,改數據 | INSERT/UPDATE/DELETE |
| MDL_SHARED_NO_WRITE (SNW) | 允許讀、阻塞寫 | ALTER TABLE(大部分階段) |
| MDL_SHARED_NO_READ_WRITE (SNRW) | 訪問字典+讀寫數據,阻塞其他讀寫 | LOCK TABLE ... WRITE |
| MDL_EXCLUSIVE (X) | 排他,可改字典與數據 | DROP TABLE, TRUNCATE |
亮點:ALTER TABLE 使用 SNW,允許並發 SELECT(SR)通過,大幅提升大表變更期間的可用性。
IX S SH SR SW SNW SNRW X
IX 1 1 1 1 1 1 1 1
S 1 1 1 1 1 1 1 0
SH 1 1 1 1 1 1 1 0
SR 1 1 1 1 1 1 0 0
SW 1 1 1 1 1 0 0 0
SNW 1 1 1 1 0 0 0 0
SNRW 1 1 1 0 0 0 0 0
X 1 0 0 0 0 0 0 0
注意:X 與 IX 兼容,因這裡的 IX 僅用於範圍鎖,非傳統意向鎖。
MDL_context:線程級上下文,持有該線程所有鎖請求(THD::mdl_context)。MDL_request:單個鎖請求(對象 + 類型)。MDL_ticket:排隊實體,請求最終獲取的「入場券」,含指向 MDL_lock 的指針。MDL_lock:全局鎖資源,static MDL_map mdl_locks 哈希表按對象唯一鍵存儲,受 mysql_mutex_lock(&m_mutex) 保護。Acquire_lockif (request 已持有同類或更強 ticket) return ticket;
create new ticket;
if (lock 不存在於 mdl_locks) create lock;
if (lock.can_grant(request)) {
grant_lock(ticket, lock);
attach_ticket(request, ticket);
} else {
enqueue_wait(ticket, lock);
wait_for_grant(ticket);
}
if (compatible(lock.granted_set, req_type)) {
if (compatible(lock.waiting_set, req_type)) // wait 矩陣與 granted 矩陣不同!
return CAN_GRANT;
}
return CANNOT_GRANT;
關鍵細節:waiting_set 兼容性檢查非冗餘——其矩陣更嚴格,目的是「DDL 等待時阻斷新 DML 入隊」,防止飢餓。
lock.m_waiting 隊列,線程休眠於 MDL_wait::m_cond。release_all_locks_for_name → reschedule_waiters,遍歷等待隊列,對可授予者:
m_waiting.remove(ticket)m_granted.add(ticket)ticket.ctx->m_wait.set_status(GRANTED) 喚醒線程Visual Studio / CLion 等 IDE 的 「凍結線程」 功能可精確控制並發時序,復現死鎖/飢餓場景,配合 MDL 內部狀態檢查,是鎖模塊調試的利器。
源碼版本:MySQL 5.7 / 8.0,核心文件 sql/mdl.cc、sql/mdl.h。