truncate是ddl而非dml,本质是重建表而非逐行删除:它不写undo log、不可回滚、不触发触发器、重置自增计数器、要求drop权限且会隐式提交事务。

TRUNCATE 是 DDL,不是“删数据”而是“重建表”
TRUNCATE 在 MySQL(以及 Oracle、SQL Server)中被归类为 DDL(数据定义语言),它的实际行为不是逐行删除记录,而是直接丢弃原表的数据段(segment),再新建一个空的同结构表。这个过程绕过了 InnoDB 的事务系统:不写 undo log,也不进事务日志队列。所以一旦执行,就立即生效,ROLLBACK 根本没东西可撤。
DELETE 是 DML,靠 undo log 实现回滚
DELETE 属于 DML(数据操纵语言),每删一行,InnoDB 都会在 undo log 中保存旧值,并在 redo log 中记录页变更。这意味着:
- 即使
autocommit=1,每条DELETE FROM t也自动成一个独立事务 - 在显式事务中(
BEGIN后),你可以随时ROLLBACK撤销整批删除 - 它会触发
BEFORE/AFTER DELETE触发器,也会逐条校验外键约束
隐式提交是 TRUNCATE 最容易踩的坑
很多人以为只要在事务里写 TRUNCATE TABLE t 就能受事务控制——其实不是。MySQL 会在执行 TRUNCATE 前**自动提交当前事务**,然后才执行清空操作。所以你后面跟 ROLLBACK 已经无效了。
常见错误现象:
-
BEGIN; TRUNCATE TABLE t; ROLLBACK;→ 表已空,无法恢复 -
TRUNCATE TABLE t WHERE id > 100→ 直接报错ERROR 1064 (42000),语法不支持条件 - 对有外键引用的表执行
TRUNCATE→ 报错ERROR 1701 (HY000),而DELETE可以走约束检查流程
权限和锁行为完全不同,不是快慢问题,是能不能做
别只盯着“TRUNCATE 更快”,它要求的是 DROP 权限(不是 DELETE 权限),执行时拿的是表级 SCH_M 锁——整个表瞬间不可读不可写。而 DELETE 持有 IX 锁 + 行锁,其他会话还能读(MVCC 可见),只是写冲突行会被阻塞。
真正决定用哪个的,从来不是“想不想快”,而是:
- 是否允许回滚?→ 必须用
DELETE - 是否需要重置自增 ID?→ 只能用
TRUNCATE - 是否有触发器逻辑要执行?→ 只能用
DELETE - 是否被外键引用?→
TRUNCATE直接失败,DELETE可能成功(取决于约束动作)
生产环境里,看到 TRUNCATE 就得确认三件事:权限够不够、有没有外键、事务里有没有其他还没提交的操作——少看一眼,表就真没了。










