TRUNCATE TABLE比DELETE执行速度更快,因其是DDL操作,直接释放数据页、不写undo log、不触发事务回滚,而DELETE是DML操作,逐行删除并记录日志。

优先用 TRUNCATE TABLE,不是“可以考虑”,而是“必须首选”——只要它能跑通,就别碰 DELETE FROM。
为什么 TRUNCATE TABLE 几乎瞬时完成,而 DELETE 卡死在 Updating 状态
因为 TRUNCATE TABLE 不是删数据,是删表重建:直接释放所有数据页、清空聚簇索引、重置 AUTO_INCREMENT 计数器,不写 undo log、不走事务引擎的行级锁路径。而 DELETE FROM big_table 是逐行扫描 + 逐行标记删除 + 写 binlog + 写 undo + 触发器检查 —— 千万级表上,光是加 next-key lock 就能让其他写请求排队等死。
常见错误现象:SHOW PROCESSLIST 显示状态为 Updating 或 Writing to net,执行时间从几秒涨到几分钟,innodb_row_lock_time_avg 暴涨,主从延迟飙升。
-
TRUNCATE是 DDL,隐式提交,不受当前事务控制(哪怕你包在BEGIN里也立刻提交) -
DELETE是 DML,长事务会撑爆innodb_log_file_size和undo_log,甚至触发 OOM killer - InnoDB 表上,
TRUNCATE不触发ON DELETE CASCADE或任何BEFORE/AFTER DELETEtrigger
TRUNCATE TABLE 执行失败的三个典型报错及应对
不是所有表都能直接 TRUNCATE,卡住时先看错误码,别硬 retry。
-
ERROR 1701 (HY000): Cannot truncate a table referenced in a foreign key constraint:该表被其他表的外键引用。可临时禁用约束:SET FOREIGN_KEY_CHECKS = 0;→TRUNCATE TABLE t;→SET FOREIGN_KEY_CHECKS = 1;;但务必确认下游无级联依赖逻辑 -
ERROR 1142 (42000): TRUNCATE command denied to user:账号没DROP权限(TRUNCATE实际需要DROP权限,不是DELETE权限)。联系 DBA 授权,或切回DELETE分批删 - 执行阻塞、无报错、长时间无响应:查
INFORMATION_SCHEMA.INNODB_TRX找长事务,或SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_NAME = 't';看是否有未释放的 MDL 锁
TRUNCATE 后磁盘空间没变小?这不是 bug,是 InnoDB 的正常行为
TRUNCATE TABLE 只把数据页标记为“可复用”,不会立即把空间还给操作系统 —— .ibd 文件大小不变是常态,尤其当 innodb_file_per_table = ON 时更明显。
真正收缩物理文件,必须补一步:OPTIMIZE TABLE t;。但它会重建整张表、锁表(即使在线 DDL 也有短暂阻塞),且只对独立表空间生效。
- 执行前确认:
SHOW VARIABLES LIKE 'innodb_file_per_table';必须为ON - MySQL 5.7+ 中,
OPTIMIZE TABLE对 InnoDB 等价于ALTER TABLE t FORCE,非低峰期勿操作 - 若表太大(>50GB),
OPTIMIZE风险高,改用原子切换法:CREATE TABLE t_new LIKE t;→RENAME TABLE t TO t_bak, t_new TO t;→ 确认后DROP TABLE t_bak;
误删后没备份?TRUNCATE 比 DELETE 更难抢救
TRUNCATE 不进 undo log,不进 binlog(MySQL 5.7+ 默认记录 DDL,但仍是 statement 格式,不可用于行级恢复),ROLLBACK 无效,FLASHBACK 工具基本失效。唯一指望是:全量备份 + 增量 binlog(需开启 log_bin 且保留足够久)。
所以线上执行前必须做三件事:
- 用
SELECT COUNT(*) FROM t;确认数据量,避免在亿级表上盲目操作 - 查外键依赖:
SELECT CONSTRAINT_NAME, TABLE_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 't'; - 确认业务方已下线对该表的读写(尤其缓存层可能还在用旧数据)
别信“我刚清空完,现在赶紧导出还能救回来”——TRUNCATE 后 SELECT 返回空集,不代表数据还在磁盘上待命。


















