「✓」=兼容 (可並發持有)、「✗」=衝突 (阻塞等待)、「→」=申請方向
| 申請 \ 持有 | IS | S | SH | SW | X |
|---|---|---|---|---|---|
| IS (意圖共享) | ✓ | ✓ | ✓ | ✓ | ✗ |
| S (共享) | ✓ | ✓ | ✓ | ✗ | ✗ |
| SH (高優共享) | ✓ | ✓ | ✓ | ✗ | ✗ |
| SW (共享寫) | ✓ | ✗ | ✗ | ✗ | ✗ |
| X (排他) | ✗ | ✗ | ✗ | ✗ | ✗ |
-- 會話 1:長事務 (未提交)
BEGIN;
SELECT * FROM t WHERE id = 1 FOR UPDATE; -- 隱式持有表級 MDL_SHARED (IS)
-- 業務邏輯處理 10 分鐘...
-- 會話 2:DDL
ALTER TABLE t ADD COLUMN c INT; -- 申請 MDL_EXCLUSIVE,被會話 1 阻塞,進入等待隊列
-- 會話 3-N:正常查詢
SELECT * FROM t; -- 申請 MDL_SHARED (IS),發現隊列中有 X 等待,**為防飢餓,MySQL 讓查詢在 X 之後排隊**
-- 結果:會話 2 等待會話 1,會話 3-N 等待會話 2,**整表所有查詢掛起**,業務癱瘓
-- 備份腳本執行
FLUSH TABLES WITH READ LOCK; -- 獲取 GLOBAL MDL_SHARED
-- 此時所有寫入 (DML/DDL) 全部阻塞
-- mysqldump --single-transaction 只會瞬間持有此鎖獲取 binlog 位點
-- 無 --single-transaction 會持有整個備份過程,**生產嚴禁**
-- 表 child 有 FK 指向 parent
INSERT INTO child ...; -- 除自身 IS 鎖外,需檢查 parent 獲取 MDL_SHARED_HIGH_PRIO
-- 若 parent 正在 DDL (X 鎖),INSERT 阻塞
-- 若 parent 有長事務 (S 鎖),INSERT 可並發 (SH 兼容 S)
-- 1. 查看當前所有 MDL 持有/等待 (5.7+ performance_schema)
SELECT
OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME,
LOCK_TYPE, LOCK_DURATION, LOCK_STATUS,
THREAD_ID, PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_INFO
FROM performance_schema.metadata_locks
WHERE LOCK_STATUS = 'PENDING' OR OBJECT_NAME = 'your_table'\G
-- 2. 結合 processlist 定位阻塞源頭
SELECT * FROM information_schema.processlist
WHERE STATE LIKE '%metadata lock%' OR INFO LIKE '%ALTER%'\G
-- 3. 歷史 DDL 執行記錄 (需開啟 general_log 或 audit_log)
pt-online-schema-change / gh-ost,**完全繞過 MDL_EXCLUSIVE**,原理:建影子表→同步數據→原子切換SET GLOBAL lock_wait_timeout = 30; (默認 31536000 秒 = 1 年!)metadata_locks 中 LOCK_STATUS='PENDING' 持續 > 5s 即告警ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE (5.6+)核心心法: **MDL 是保護元數據一致性的「大鎖」,DDL 拿 X 鎖是核武器,長事務是誘發核爆的導火索。生產環境零容忍長事務,DDL 必走在線工具。**