SELECT被ALTER TABLE卡住是因为MDL_SHARED_READ与MDL_EXCLUSIVE互斥,非行锁冲突;只要会话访问表(含单条SELECT),就持MDL读锁,ALTER需等其释放。

MySQL发生Waiting for table metadata lock不是数据库“卡了”,而是DDL和DML在抢同一张表的元数据锁(MDL),本质是锁兼容性规则生效——不是故障,是保护机制在起作用。
为什么SELECT会被ALTER TABLE卡住
这不是行锁冲突,也不是连接数满了,而是MySQL自动加的MDL_SHARED_READ和MDL_EXCLUSIVE互斥导致的。只要一个会话正在访问表(哪怕只是SELECT * FROM t LIMIT 1),另一个会话想执行ALTER TABLE就必须等它释放MDL读锁。
- 长事务中未提交的
SELECT最危险:它会让MDL_SHARED_READ锁持续持有,直到事务结束 - 自动提交模式下的单条
SELECT也会加MDL锁,但执行完立刻释放,通常观察不到阻塞 -
mysqldump --single-transaction备份期间若有长事务,也会拖慢MDL释放 - MySQL 8.0起强化了MDL一致性校验,不再允许“跳过”已有读锁执行DDL
如何快速定位谁在持锁、谁在等锁
别只看SHOW PROCESSLIST——它不显示等待链。必须查performance_schema,且要确认它已启用:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 先检查:
SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema';返回ON才有效 - 查等待链(MySQL 8.0):
SELECT * FROM performance_schema.data_lock_waits; - 查持锁事务(通用):
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' AND TIME > 60; - 轻量兜底查法:
SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist WHERE state = 'Waiting for table metadata lock' ORDER BY time DESC;,再顺藤摸瓜找对应表上State非Waiting的活跃会话
终止进程前必须确认的三件事
KILL操作不可逆,误杀可能中断业务事务。执行前务必核对:
- 目标
Id是否属于非核心应用用户(如app_user、backup),避免KILL掉DBA或监控账号 - 该会话的
Info是否为明显无意义的长查询(如SELECT SLEEP(3600))或已断连残留(Command为Sleep且Time超30分钟) - 是否存在多个会话同时阻塞在同一张表?优先KILL最早进入
Waiting for table metadata lock的那个,而不是最后一个
真正难处理的不是“怎么解锁”,而是“为什么锁能持有一小时还不释放”——这往往暴露了应用层事务管理缺陷:忘记COMMIT/ROLLBACK、连接池配置不当、监控脚本长期占连接。MDL锁本身没有bug,它只是把代码里的松懈照得特别清楚。










