TRUNCATE TABLE卡在Waiting for table metadata lock是因为其需MDL_EXCLUSIVE锁,而活跃事务(含Sleep中未提交的SELECT)持有SHARED_READ锁阻塞;须通过performance_schema.metadata_locks查LOCK_STATUS='GRANTED'定位持锁线程,优先KILL QUERY再KILL CONNECTION。

TRUNCATE TABLE 为什么卡在 Waiting for table metadata lock
因为 TRUNCATE TABLE 是 DDL 操作,必须获取表的 MDL_EXCLUSIVE 锁,而只要有任何活跃事务(哪怕只是 SELECT)正在访问该表,就无法拿到这个锁——它不是“表被锁了”,而是“有人正拿着读锁不放”。
常见错误现象:SHOW PROCESSLIST 中看到状态为 Waiting for table metadata lock,Info 字段显示 TRUNCATE TABLE xxx,但查不到明显正在执行的长查询。
- 真正持锁者往往是个
Command = 'Sleep'、Time > 300、Info为空的连接 - 这个连接可能只执行过一条
BEGIN,然后卡住或断开未提交,但它持有的 MDL_SHARED_READ 锁一直没释放 - 即使
INNODB_TRX里看不到该线程的事务记录,也要查performance_schema.metadata_locks,因为 MDL 锁不依赖 InnoDB 事务状态
怎么快速定位 TRUNCATE 的真实阻塞源
别只盯着 SHOW PROCESSLIST 里那个显示 Waiting for table metadata lock 的线程——它只是受害者。你要找的是 LOCK_STATUS = 'GRANTED' 的那条记录。
先确认 performance_schema 已启用:
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';
确保 ENABLED 和 TIMED 都是 YES。再执行:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST<br>FROM performance_schema.metadata_locks m<br>JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID<br>WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table'<br>AND LOCK_STATUS = 'GRANTED';
- 重点关注
LOCK_TYPE为SHARED_READ或SHARED_WRITE的行,对应的就是真正在持锁的连接 - 拿到
PROCESSLIST_ID后,立刻查INNODB_TRX确认该线程是否处于trx_state = 'RUNNING' - 如果查不到,再查
performance_schema.events_statements_current,看它最近执行了什么语句(比如字段不存在的 SELECT,失败后仍占锁)
KILL 前为什么必须分两步:先 KILL QUERY 再 KILL CONNECTION
直接 KILL [id] 可能触发大事务回滚,反而让锁持有时间更长、影响面更大;而 KILL QUERY [id] 只中断当前语句,保留连接和事务上下文,给应用重试机会。
- 优先执行
KILL QUERY [processlist_id],观察TRUNCATE是否立即开始执行 - 如果
KILL QUERY无效(比如线程已卡死),再执行KILL [processlist_id] - 注意:
TRUNCATE自身一旦进入system lock状态(常出现在 MySQL 5.7/8.0 早期版本),KILL 也可能无法立刻生效,此时需结合INNODB_TRX和PROCESSLIST综合判断是否真有悬挂事务
TRUNCATE 引发 system lock 的隐性风险
system lock 不是表级锁,而是 MySQL 内部资源争用的表现,尤其在大表 TRUNCATE 时会触发 buffer pool 扫描、查询缓存失效等全局操作——它会让整个实例响应变慢,甚至看起来像“夯住”。
- MySQL 5.7 中
TRUNCATE仍沿用旧式 buffer pool 清理逻辑,不像DROP TABLE那样做了异步优化 - 如果表上有外键约束、触发器或分区定义,
TRUNCATE还要额外校验元数据一致性,进一步拉长锁持有时间 - 线上高频执行
TRUNCATE的场景,建议改用DELETE FROM ... LIMIT N分批清理 +OPTIMIZE TABLE,避开 MDL 尖峰
最易被忽略的一点:MDL 锁生命周期绑定事务,而不是语句。哪怕你只在存储过程中写了 SELECT,只要没 COMMIT,TRUNCATE 就永远等在那儿。


















