truncate table只申请表级x锁,不走行锁或间隙锁,是ddl操作,不维护mvcc版本链,加锁→释放数据页→更新元数据→解锁全过程毫秒级完成,不生成undo log,不可回滚,遇mdl锁冲突立即报错而非等待。

TRUNCATE TABLE 只申请表级 X 锁,不走行锁或间隙锁
TRUNCATE 是 DDL 操作,不是 DML,它不逐行处理数据,也不维护 MVCC 版本链。执行时只向表申请一次 table-level exclusive lock(表级排他锁),用于安全修改元数据(如重置 auto_increment、清空数据页指针等)。锁持有时间极短——通常在毫秒级完成“加锁→释放数据页→更新元数据→解锁”全过程。你不会在 INFORMATION_SCHEMA.INNODB_TRX 里看到对应事务记录,也不会在 SHOW ENGINE INNODB STATUS 中看到 RECORD LOCKS 或 LOCK_WAIT。
遇到 MDL 锁冲突会直接报错,而不是等待
TRUNCATE 不会排队等锁,而是立刻检测元数据锁(MDL)是否可用。如果表正被其他事务以 SELECT ... FOR UPDATE、LOCK TABLES t WRITE 占用,或有活跃的长事务正在访问该表,TRUNCATE 会立即失败,报类似:
ERROR 1099 (HY000): Table 't' was locked with a READ lock and can't be truncated
或
ERROR 1205 (HY000): Deadlock found when trying to get lock
这类错误说明它压根没进入锁等待队列,和 DELETE 在同样场景下可能卡住几十秒形成鲜明对比。
system lock 状态本质是 MDL 获取失败后的阻塞表现
当你在 SHOW PROCESSLIST 中看到 TRUNCATE 处于 system lock 状态,实际是它在等待获取 MDL_EXCLUSIVE 锁,但被其他会话持有的 MDL_SHARED(比如未提交的 SELECT)或 MDL_SHARED_WRITE(比如正在执行的 INSERT/UPDATE)阻塞。这不是“系统级全局锁”,而是元数据锁层面的等待——但影响范围广,因为所有后续需要访问该表元数据的操作(包括 SELECT、INSERT、甚至其他表的 DDL)都会被连锁阻塞。
-
system lock不代表锁住了整个实例,但会阻塞所有依赖该表元数据的并发操作 - 在 MySQL 5.7+ 中,这种状态常伴随
Waiting for table metadata lock出现在STATE字段 - 杀掉阻塞源事务(而非 TRUNCATE 本身)才能快速释放,否则 kill TRUNCATE 可能残留
killed状态且无法清理
TRUNCATE 的“快”来自绕过事务锁机制,不是锁粒度小
真正容易被忽略的是:TRUNCATE 的低延迟不源于它“锁得更细”,而在于它完全不参与 InnoDB 的事务锁管理流程。它不生成 undo log,不写 row-based binlog(除非开启 binlog_format=STATEMENT),不触发 gap lock,也不受隔离级别影响。这意味着:
- 你不能把它包在
BEGIN/ROLLBACK里——执行即隐式提交 - 外键约束检查失败时直接退出,不会尝试降级或重试
- 分区表上执行
TRUNCATE TABLE t会清空所有分区,但保留分区定义;而TRUNCATE TABLE t PARTITION (p1)在 MySQL 8.0+ 才支持,旧版本只能用ALTER TABLE ... DROP PARTITION+REORGANIZE
线上高频执行 TRUNCATE 前,务必确认没有长事务、无活跃外键引用、且 binlog 格式与复制策略兼容——它的快,是以放弃事务可控性为代价的。











