mysql 8.0升级后metadata lock锁等待激增,根本原因是旧版本中“装睡”的未提交事务(sleep状态但trx_state='running')在新版本更严格的状态跟踪下暴露,持续持有mdl锁阻塞ddl;需通过innodb_trx、performance_schema.metadata_locks等交叉定位并终止。

不是锁变多了,是原来“装睡”的事务在 8.0 下暴露了——它们状态是 Sleep,但 trx_state = 'RUNNING',持续持有 MDL 锁不放。
为什么 sys.innodb_lock_waits 查询会卡住?
MySQL 8.0 中该视图底层依赖 performance_schema.data_locks 和 data_lock_waits,而这两张表在读取时会主动申请 MDL 锁。一旦有 DDL(如 ALTER TABLE)正在执行,监控脚本就会被阻塞在 Waiting for table metadata lock,进而拖慢整个连接池。
- 这不是你写的 SQL 慢,是查询系统视图本身触发了元数据锁竞争
- 尤其当大事务锁住几十万行时,
data_locks可能返回海量记录,扫描+加锁耗时剧增 - 5.7 的
information_schema.INNODB_LOCK_WAITS是轻量只读视图,8.0 已弃用,但直接切过去仍需适配逻辑
如何快速定位真正持锁的“僵尸事务”?
别只看 SHOW PROCESSLIST —— 真正持锁的线程往往显示为 Command = 'Sleep'、Time 很大(比如 1800+ 秒),但 INFO 为空。必须交叉查三张表:
- 先查
information_schema.INNODB_TRX,筛选:trx_autocommit = 0且trx_state = 'RUNNING'且trx_query IS NULL,按trx_started倒序找最老的几个 - 用查到的
trx_mysql_thread_id去performance_schema.threads找对应THREAD_ID - 再查
performance_schema.metadata_locks,确认该THREAD_ID是否对目标库表持有LOCK_TYPE = 'SHARED_READ'或'SHARED_WRITE'
DROP VIEW 或 ALTER TABLE 卡在 Waiting for table metadata lock 怎么办?
视图本身不存数据,但删除或修改它需要 MDL_EXCLUSIVE 锁;只要任何会话还在用它或它的基表(哪怕只是个未提交的 SELECT),就会被堵住。
- 执行
DROP VIEW前,先跑:SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_view_name';
- 若发现
LOCK_STATUS = 'GRANTED'且LOCK_DURATION = 'TRANSACTION',说明某个事务正持有锁 - 不要直接
KILL,先尝试KILL QUERY thread_id(虽对 Sleep 无效,但可排除它正在跑长查询) - 等 10–20 秒后,再查
INNODB_TRX,若仍是RUNNING且trx_query IS NULL,才执行KILL CONNECTION thread_id
预防比抢救更关键:升级后必须改的三个配置
MySQL 8.0 默认 wait_timeout = 28800(8 小时),而很多 Python/Java 应用用 pymysql 或 aiomysql 连接,默认 autocommit=False,一条 SELECT 后不 commit() 也不 close(),锁就挂着不动。
- 全局设
wait_timeout = 300(5 分钟)、interactive_timeout = 300,让空闲连接自动断开 - 应用层确保所有
autocommit=False的连接,执行完语句后显式调用commit()或rollback() - 禁用
performance_schema中非必要 consumers,如events_statements_history_long,减少锁开销
真正难处理的从来不是锁本身,而是那些没人记得自己开了事务、也没人知道它还活着的连接——它们在 5.7 里能靠超时“自然死亡”,到了 8.0,得靠配置和习惯把它们提前掐死。











