执行 ALTER TABLE、建索引或修改字段时,语句长时间没有完成,进程列表显示 Waiting for table metadata lock。继续提交新的查询后,连普通读写也开始排队,网站随之变慢。
这个现象不是数据库正在慢慢修改表,而是DDL仍在等待获得对象的元数据锁。MySQL用元数据锁保护表、库、存储程序等对象的一致性;一个看似空闲但没有结束的事务,也可能让后续DDL一直等待。

先保存等待现场
先记录当前连接、等待时间和完整语句:
SHOW FULL PROCESSLIST;
重点找State为 Waiting for table metadata lock 的会话,记下连接ID、用户、来源、数据库、等待时长和SQL。不要立刻重启MySQL,否则等待链和持锁者会一起消失,事后只剩业务恢复过的表象。
还要记录故障开始时间、刚执行的发布或DDL,以及应用连接数变化。等待者通常不是根因,真正要找的是同一对象上已经获得锁却未释放的会话。
从metadata_locks区分等待与持有
MySQL 8可以读取Performance Schema中的元数据锁:
SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME,
LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID
FROM performance_schema.metadata_locks
WHERE OBJECT_SCHEMA = 'app_db'
AND OBJECT_NAME = 'orders';
把库名和表名换成现场对象。PENDING 表示锁请求尚未获得,GRANTED 表示已获得;需要通过对象、锁类型和线程ID建立等待关系,而不是看到任意GRANTED记录就结束会话。
元数据锁检测默认启用的版本可以直接查询。表为空或权限不足时,先确认Performance Schema配置和账号权限,不要用猜测补齐证据。
把线程ID映射到真实连接
OWNER_THREAD_ID 不是 KILL 使用的连接ID。可与线程表和语句表关联,找到实际进程:
SELECT t.THREAD_ID, t.PROCESSLIST_ID, t.PROCESSLIST_USER,
t.PROCESSLIST_HOST, t.PROCESSLIST_TIME, t.PROCESSLIST_STATE,
t.PROCESSLIST_INFO
FROM performance_schema.threads AS t
WHERE t.THREAD_ID IN (
SELECT OWNER_THREAD_ID
FROM performance_schema.metadata_locks
WHERE OBJECT_SCHEMA = 'app_db' AND OBJECT_NAME = 'orders'
);
同时检查事务表:
SELECT trx_mysql_thread_id, trx_started, trx_state,
trx_tables_locked, trx_rows_modified, trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;
常见持锁者是应用开启事务后长期停留、管理工具关闭窗口却没有提交,或批处理仍在访问目标表。会话显示Sleep不代表它没有未提交事务。
为什么新的普通查询也会排队
一个长事务先持有共享元数据锁,DDL随后请求排他的元数据锁并进入等待。之后到来的查询可能又排在DDL后面,于是原本只影响结构变更的问题扩散成业务请求堆积。
此时只终止等待中的DDL,可能让后续查询暂时恢复,但长事务仍然存在;只扩大连接池,则会让更多请求进入等待。必须先画出持锁者、DDL等待者和后续会话的顺序。
终止会话前先评估事务影响
最理想的处理是让持锁业务正常提交或回滚。来源明确、事务可重试时,才评估终止对应连接:
KILL CONNECTION connection_id;
不要把 OWNER_THREAD_ID 直接填入KILL,也不要批量终止所有Sleep连接。包含大量未提交修改的事务被终止后需要回滚,回滚期间资源压力仍可能很高;支付、订单等关键事务还要先确认业务幂等与重试机制。
生产环境中的DDL也应评估锁等待超时、在线DDL能力、表大小和维护窗口。显示online并不意味着完全不需要元数据锁,开始和结束阶段仍可能受并发事务影响。
从应用侧修复长期事务
锁解除只是现场恢复,根因通常在事务边界。继续检查应用是否在等待外部接口或用户操作时保持数据库事务,异常路径是否遗漏提交和回滚,连接池归还连接前是否清理事务状态。
为关键事务记录开始、提交、回滚和请求标识,并设置符合业务的事务超时。定期观察长事务和元数据锁等待,比在发布时临时KILL连接更稳妥。
旧数据库版本、连接池和业务代码同时变化时,可以在 萤光云 准备同版本数据库复现事务,也可以使用按小时计费的 LightNode 建立隔离验证环境。只复制脱敏结构和必要测试数据,不要把生产凭据或完整客户数据带入对照环境。
验收标准要覆盖下一次DDL
处理后重新读取 SHOW FULL PROCESSLIST、metadata_locks 和 innodb_trx,目标对象不应再存在长期PENDING记录,异常长事务已经提交、回滚或得到业务解释。
随后在维护窗口重试原DDL,记录开始、获得锁和完成时间;应用请求没有随之堆积,连接数和响应时间保持正常。最后修复应用事务边界,并通过压测或回归测试确认异常分支会正确回滚。
只让本次ALTER执行完成,还不能算关闭问题。能解释持锁来源、修复事务生命周期,并让下一次结构变更可控完成,证据链才完整。


