truncate快是因为它本质是“换表”而非删数据:释放原表段并新建空段,跳过行级锁、undo log、redo log等dml开销,仅记一条ddl日志,不触发触发器,且隐式提交、不可回滚。

TRUNCATE 为什么根本不需要走行级锁路径
因为 TRUNCATE TABLE 不是“删数据”,而是“换表”——它调用的是 InnoDB 的 DDL 接口 ha_innobase::truncate(),直接释放原表 segment 并新建一个空 segment。整个过程不访问聚簇索引、不扫描任何数据页、不遍历 B+ 树,自然没有逐行加锁的必要。
DELETE 为什么必须逐行加锁
DELETE FROM t 是标准 DML 操作,InnoDB 必须保证事务一致性与 MVCC 可见性,所以哪怕没 WHERE 条件,也会:
- 启动隐式事务(
autocommit=1下也如此) - 全表扫描聚簇索引,对每一行加 X 锁(或 next-key lock)
- 写 undo log 记录反向镜像,用于回滚和快照读
- 触发外键约束检查、BEFORE/AFTER DELETE 触发器
- 每行变更都生成 redo log,并刷入 log buffer
千万级表上,光是加锁和日志写入就足以让 innodb_row_lock_time_avg 暴涨,其他写请求排队阻塞。
TRUNCATE 的锁只是瞬时元数据锁
TRUNCATE 确实会加锁,但不是行锁或间隙锁,而是极短时间持有的表级 SCH_M(schema modification)锁,仅用于保护元数据变更安全。它的生命周期是:
- 获取
SCH_M锁(期间表不可读不可写) - 释放 ibd 文件对应的数据段
- 重建空 segment、重置
AUTO_INCREMENT - 立刻释放锁,不等待刷盘或事务提交
这个过程通常在毫秒级完成,SHOW PROCESSLIST 几乎看不到停留,INFORMATION_SCHEMA.INNODB_TRX 里也查不到对应事务。
容易被忽略的关键限制
别只盯着“快”,先确认能不能跑通:
-
ERROR 1701 (HY000):表被外键引用 → 必须先SET FOREIGN_KEY_CHECKS = 0,但要承担级联失效风险 -
ERROR 1142 (42000):账号缺DROP权限(不是DELETE权限)→ DBA 得授权DROP - 不能在事务块里用:
BEGIN; TRUNCATE TABLE t; ROLLBACK;→TRUNCATE会自动提交,ROLLBACK无效 - 不支持
WHERE:写成TRUNCATE TABLE t WHERE id > 100直接报ERROR 1064,语法层面拒绝解析











