mdl锁是mysql 5.5起引入的表级元数据锁,用于保证ddl与dml并发时结构与数据的一致性;它自动加锁、不可干预,读锁(如select)与写锁(如alter)互斥,且写锁优先级更高,易引发阻塞。

分区表过多本身不直接引发元数据锁(MDL),但会显著放大 MDL 问题的暴露频率和严重程度——根本原因在于每个分区都独立参与 MDL 管理,且大量分区导致 performance_schema.metadata_locks 视图膨胀、锁状态查询变慢甚至超时,掩盖真实阻塞源。
为什么分区多会让MDL问题更难排查?
MySQL 5.7 对每个分区都单独申请一次 MDL 锁(哪怕只是 SELECT),这意味着:
- 一个含 64 个分区的表被
BEGIN; SELECT * FROM t WHERE dt = '2026-01-01';访问时,可能触发最多 64 次 MDL_SHARED_READ 申请(取决于分区裁剪是否生效) -
performance_schema.metadata_locks表中该表相关记录数 ≈ 分区数 × 并发线程数,容易撑满内存或拖慢查询 -
SHOW PROCESSLIST显示Waiting for table metadata lock,但无法看出是卡在“整个表”还是“某个具体分区”上 - 常见误判:把
ALTER TABLE ... REORGANIZE PARTITION卡住归因为“DDL 冲突”,实际是它在逐个分区加 MDL_WRITE 锁,而某个分区正被长事务持有读锁
必须先确认分区数量是否已越界
硬性阈值不是性能建议,而是启动/运行底线:
- 执行
SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';,结果 > 128 → 直接触发open_files_limit耗尽,metadata_locks可能无法完整采集 - 即使未超 128,若单表分区数 ≥ 96,需检查
table_open_cache是否足够(建议 ≥ 分区数 × 2) - 用
EXPLAIN PARTITIONS SELECT ...验证查询是否真正命中目标分区;若显示partitions: p0,p1,...,p63,说明分区裁剪失效,所有分区都会参与 MDL 争抢
定位真实持锁分区的实操路径
别依赖 INNODB_TRX 查“哪个表”,要落到“哪个分区”:
- 先查持锁线程:
SELECT THREAD_ID, OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table'; - 若
INDEX_NAME为空且LOCK_TYPE是TABLE,说明锁在整表粒度(常见于未走分区裁剪的查询) - 若
OBJECT_NAME显示为your_table#P#p12(MySQL 内部命名),则锁定的就是p12分区 —— 这才是真正的阻塞点 - 关联
information_schema.PROCESSLIST找出对应ID,再查INNODB_TRX确认其trx_state = 'RUNNING'且trx_started时间异常久
修复时避免雪球效应
分区表上的 MDL 问题一旦爆发,操作顺序错了会连锁恶化:
- 禁止直接对大分区表执行
ALTER TABLE ... ADD PARTITION:该操作需全表 MDL_EXCLUSIVE 锁,会卡住所有并发查询 - 合并分区优先用
ALTER TABLE ... REORGANIZE PARTITION p1,p2 INTO (PARTITION p12 VALUES LESS THAN (...)),它只锁涉及的分区,不影响其他分区 - 如果必须退化为普通表,先停写,再
ALTER TABLE ... REMOVE PARTITIONING,否则过程中新写入可能因分区规则失效而报错 -
OPTIMIZE PARTITION在 InnoDB 中实际重建整个表,等同于全表锁,5.7 中应改用ANALYZE PARTITION+REBUILD PARTITION组合
最易被忽略的一点:应用层连接池归还连接前未 COMMIT,会导致某一分区长期挂着 MDL_SHARED_READ,而 PROCESSLIST 显示为 Sleep,只有 JOIN INNODB_TRX 才能揪出——这种“静默持锁”在分区多的表上影响面会被指数级放大。











