TRUNCATE卡在“System lock”是在等待元数据锁(MDL),而非表级或行级锁;它需获取EXCLUSIVE MDL锁,若存在未释放的SHARED_READ/SHARED_WRITE MDL锁(如未提交事务或长查询),TRUNCATE即阻塞,并连锁阻塞所有后续对该表的访问。

TRUNCATE卡在“System lock”就是在等元数据锁
这不是表级锁,也不是InnoDB行锁,而是MySQL Server层的元数据锁(MDL)。TRUNCATE TABLE是DDL操作,必须获取对表的EXCLUSIVE MDL锁,才能安全地重置表定义、释放段、清空.frm/.ibd文件。只要该表上有任何未释放的SHARED_READ或SHARED_WRITE MDL锁(比如一个没提交的SELECT、INSERT、UPDATE事务,甚至一个长查询),TRUNCATE就会挂起在System lock状态,且所有后续访问该表的语句——包括SELECT——都会被阻塞。
为什么其他表的查询也变慢了
MDL等待本身不直接锁库,但会引发连锁阻塞:当TRUNCATE卡住后,依赖它的应用线程可能堆积连接、超时重试、开启更多会话;更关键的是,持有MDL读锁的会话如果处于Sleep状态但事务未提交(trx_state = 'ACTIVE'),它不会自动释放锁,导致TRUNCATE无限期等待。此时你执行SHOW PROCESSLIST会看到大量线程停在Opening tables或Waiting for table metadata lock,看起来像“整个库卡住”。
-
lock_wait_timeout默认是31536000秒(365天),远高于innodb_lock_wait_timeout,所以等锁不会自动超时退出 - 持有锁的会话如果是
Command: Sleep但Time很大,大概率是应用开了事务却忘了COMMIT或ROLLBACK - 不要先
KILL TRUNCATE——它常卡在killed状态不退出,反而让MDL锁持续占用
怎么快速定位并清理持锁源头
核心思路:不是杀TRUNCATE,而是杀掉那个真正拿着SHARED_READ/SHARED_WRITE MDL锁的会话。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 查持锁者:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, PROCESSLIST_ID FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'table_name' AND LOCK_STATUS = 'GRANTED'; - 确认它是否真在跑事务:
SELECT * FROM information_schema.innodb_trx WHERE trx_mysql_thread_id = [PROCESSLIST_ID];,重点看trx_state和trx_started - 如果没查到活跃事务,再查它最近干了啥:
SELECT SQL_TEXT FROM performance_schema.events_statements_current WHERE THREAD_ID = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID = [PROCESSLIST_ID]); - 一旦确认是异常长事务或失效连接,立刻
KILL [PROCESSLIST_ID]
为什么SET FOREIGN_KEY_CHECKS=0不能解决MDL等待
关外键只绕过外键约束校验,不影响MDL锁机制。如果你看到TRUNCATE卡在System lock,问题一定出在MDL冲突,而不是外键。外键报错的表现是直接返回ERROR 1701 (HY000),根本不会进入等待状态。误判这一点会导致你浪费时间调FOREIGN_KEY_CHECKS,而真正持锁的长事务还在后台静默霸占资源。
最易被忽略的一点:MDL锁的生命周期绑定会话,而非事务。哪怕一个会话里只执行了一条SELECT就断开网络(没显式CLOSE),只要MySQL还没回收这个连接(比如wait_timeout未触发),它持有的SHARED_READ MDL锁就一直存在。这种“幽灵连接”最难排查,得靠PROCESSLIST里Time值和State综合判断。










