truncate 是 ddl 操作,不可回滚;它重置 auto_increment、释放数据页、清空统计信息,不写 undo log,执行前隐式提交当前事务,spring @transactional 中也无法回滚。

TRUNCATE 是 DDL,不是 DML
MySQL 把 TRUNCATE TABLE 归为数据定义语言(DDL),和 CREATE、DROP、ALTER 同级。它操作的是表的元数据结构:重置 auto_increment 计数器、释放数据页、清空统计信息,而不是逐行修改数据。因此它绕过了事务管理器,不写 undo log,自然没有回滚依据。
TRUNCATE 执行前就已隐式提交事务
哪怕你显式执行了 BEGIN 或 SET autocommit = 0,只要语句中出现 TRUNCATE,MySQL 就会在真正执行它之前,先把当前事务里所有未提交的 DML(比如前面的 INSERT、UPDATE)强制 COMMIT。之后才处理 TRUNCATE 本身——这意味着:
-
ROLLBACK只能作用于一个空事务,对已提交的 DML 和已完成的TRUNCATE都无效 - 错误提示(如表不存在)出现时,前面的 DML 早已落盘,无法补救
- Spring 的
@Transactional方法里混用TRUNCATE,异常发生后表已清空,事务回滚不包含它
所有主流引擎都不支持 TRUNCATE 回滚
截至 2026 年,InnoDB、MyISAM、MEMORY 引擎在行为上完全一致:
-
InnoDB:直接丢弃所有数据页,重置auto_increment,不记录任何行级日志 -
MyISAM:清空.MYD文件并重写头信息,元数据立即落盘 -
MEMORY:内存数据整体释放,无回滚上下文
别信“某版本/某配置下可回滚”的说法——这是设计使然,不是 bug,也不是临时限制。
想安全清空大表,得拆解真实瓶颈
真正需要事务安全又怕 DELETE 太慢时,不能简单换 TRUNCATE,而要判断瓶颈在哪:
- 若只是怕长事务锁表:
DELETE FROM table_name WHERE id BETWEEN ? AND ?分批删,每次控制在 1k~10k 行 - 若需极速清空且接受风险:确认该表无业务依赖、有备份、有快速恢复预案,再用
TRUNCATE - 若表已分区:优先考虑
ALTER TABLE DROP PARTITION,影响范围可控,部分场景还能配合EXCHANGE PARTITION实现准原子切换
关键点始终只有一个:DDL 操作没有“中间态”,一旦发出,就不再属于你的事务边界。别把它放进任何依赖回滚逻辑的流程里。











