长事务导致ddl卡在waiting for table metadata lock,因其持有mdl_shared_read锁而阻塞alter table所需的mdl_exclusive锁;需通过show processlist与information_schema.innodb_trx交叉定位长时间运行的sleep事务,并在ddl前检查锁状态、探测表可用性、设置超时及规范事务管理。

长事务为什么会让DDL卡在Waiting for table metadata lock
不是DDL本身慢,而是它被一个早已“静默”的事务按住了。只要事务没提交(哪怕只执行过一条SELECT),MySQL就会一直持有该表的MDL_SHARED_READ锁;而ALTER TABLE需要MDL_EXCLUSIVE锁,二者互斥。这时所有新来的查询、更新都会自动排到DDL后面——因为MDL排队规则是“先到先等,后到不插队”,一张表就彻底堵死。
如何快速定位正在持锁的Sleep连接
SHOW PROCESSLIST里状态为Sleep的连接,极可能就是真凶。但不能只看State,必须交叉验证它是否仍在事务中运行:
- 查
information_schema.INNODB_TRX,筛选TRX_STATE = 'RUNNING'且TRX_STARTED时间远早于当前(比如>300秒)的记录 - 用
PROCESSLIST_ID关联information_schema.PROCESSLIST,确认其COMMAND = 'Sleep'且INFO IS NULL - 重点检查
TRX_ISOLATION_LEVEL:很多框架默认用REPEATABLE-READ,BEGIN后不显式COMMIT,事务就一直挂着
DDL前必须做的三件事
别在业务高峰期直接跑ALTER TABLE,尤其当表上有定时任务或后台作业时:
- 执行前先查
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table' AND LOCK_STATUS = 'GRANTED',确认没有未释放的锁 - 对目标表做一次轻量探测:
SELECT 1 FROM your_table LIMIT 1,如果也卡住,说明已有MDL冲突,立刻中止DDL - 设置超时并限制影响范围:在会话级加
SET lock_wait_timeout = 10,避免DDL无限等待;优先用ALGORITHM=INPLACE, LOCK=NONE,但注意MODIFY COLUMN等操作仍会退化为LOCK=SHARED
应用层最容易被忽略的防御点
框架和ORM不会替你提交事务,也不会帮你兜底异常中断:
- 所有手动
BEGIN必须配对COMMIT或ROLLBACK,哪怕只是调试SQL也要养成习惯 - Spring @Transactional注解若没配置
timeout,事务可能因网络抖动、下游超时而悬停数小时 - 数据库连接池(如HikariCP)的
connection-timeout和idle-timeout只管连接生命周期,不管事务生命周期——事务开了不关,连接还回得去,锁却留着
真正危险的从来不是慢SQL,而是那个Command: Sleep、Time: 1842、Info: NULL的连接——它安静得像不存在,却能让整张表的读写集体失声。











