首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL 源碼深度解析:MDL 元數據鎖機制全景圖

     編輯:MySQL 綜合教學

MDL(Metadata Lock,元數據鎖) 是 MySQL 5.5+ 引入的鎖子系統,專門保護數據庫對象定義(表結構、視圖、觸發器等)不被並發 DDL 破壞,同時最大化 DML 並發度。本文從設計目標、鎖類型、兼容矩陣、核心數據結構、加鎖/等待/喚醒流程五個維度,系統梳理 MDL 實現邏輯(基於 sql/mdl.cc)。

MDL 是什麼?為什麼需要它?

  • 字典鎖 vs 數據鎖:MDL 保護「元數據」(表定義),行鎖保護「數據」(行記錄)。
  • 典型場景:TX1 SELECT * FROM t1 時,TX2 DROP TABLE t1 必須等待 TX1 提交/回滾,否則 TX1 可能讀到已刪除的表結構,導致 Crash 或 Binlog 邏輯錯誤。

三大設計目標

  1. 保護內存中 Table Definition Cache (TDC) 的並發訪問安全。
  2. 保證 DML 之間最大並發(如 SELECT vs INSERT)。
  3. 確保 DML 與大多數 DDL 互斥(ALTER TABLE 除外),維護 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)通過,大幅提升大表變更期間的可用性。

兼容性矩陣(1=兼容,0=衝突)

        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_lock

if (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,遍歷等待隊列,對可授予者:
    1. m_waiting.remove(ticket)
    2. m_granted.add(ticket)
    3. ticket.ctx->m_wait.set_status(GRANTED) 喚醒線程

實戰調試技巧

Visual Studio / CLion 等 IDE 的 「凍結線程」 功能可精確控制並發時序,復現死鎖/飢餓場景,配合 MDL 內部狀態檢查,是鎖模塊調試的利器。


源碼版本:MySQL 5.7 / 8.0,核心文件 sql/mdl.cc、sql/mdl.h。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved