truncate比delete快得多,因其直接释放数据页、重置元数据(含auto_increment)、不写undo log、不触发trigger、不校验外键;而delete逐行删除,需记录日志、加行锁、走完整dml流程,千万级表耗时可差10倍以上。

清空大表数据,优先选 TRUNCATE TABLE;但若需保留自增 ID 连续性、或依赖事务回滚、或表被外键引用,DELETE FROM 是唯一可行选项。
为什么 TRUNCATE 比 DELETE 快得多
TRUNCATE 不是逐行删除,而是直接释放数据页、重置表元数据(包括 auto_increment 计数器),不写 undo log、不触发 trigger、不校验外键约束。DELETE 则每删一行都记日志、加行锁、走完整 DML 流程——对千万级表,耗时可能差 10 倍以上。
常见错误现象:DELETE FROM big_table; 执行卡住、连接超时、binlog 爆涨、主从延迟飙升。
- TRUNCATE 是 DDL 操作,隐式提交,无法
ROLLBACK - TRUNCATE 后
SELECT COUNT(*)返回 0,但SHOW TABLE STATUS中Auto_increment值归 1 - MyISAM 和 InnoDB 都支持 TRUNCATE,但某些 MySQL 版本(如 5.6 早期)对有外键引用的表会报错
TRUNCATE 在大表上仍失败的典型场景
当表被其他表通过外键(FOREIGN KEY)引用,或当前会话持有该表的显式锁(如 LOCK TABLES),TRUNCATE TABLE 会直接报错:ERROR 1701 (HY000): Cannot truncate a table referenced in a foreign key constraint。
此时只能退回到 DELETE FROM,但必须分批执行,否则易引发锁表或 OOM:
- 用
WHERE+LIMIT分批次删,例如:DELETE FROM big_table ORDER BY id LIMIT 10000 - 每次删除后
COMMIT,避免长事务占满 undo log - 确保
id有索引,否则ORDER BY会全表扫描 - InnoDB 表建议配合
OPTIMIZE TABLE(仅在低峰期),否则碎片空间不释放
DELETE 清空整表时容易被忽略的陷阱
看似简单的 DELETE FROM t,在生产环境极易出问题:
- 没加
WHERE条件却误删全表——建议所有 DELETE 语句上线前强制要求带WHERE或注释说明“清空”意图 - 未关闭 autocommit 导致事务堆积,
SHOW PROCESSLIST显示Updating卡死 - 自增 ID 不重置:删完 100 万条后,下一条插入的 ID 是 1000001,而非 1;若业务逻辑依赖“ID 从 1 开始”,必须后续手动
ALTER TABLE t AUTO_INCREMENT = 1 - 触发器仍会被执行(TRUNCATE 不触发),若 trigger 里有复杂逻辑或远程调用,会拖慢删除速度
真正的大表(>50GB)别只盯着 SQL
即便用了 TRUNCATE,物理文件(.ibd)的释放也可能延迟,尤其在 innodb_file_per_table = OFF 时,空间直接归还给共享表空间,无法被 OS 回收。
更稳妥的做法是“重建表”:
- 新建空表:
CREATE TABLE t_new LIKE t - 重命名切换:
RENAME TABLE t TO t_old, t_new TO t - 再异步
DROP TABLE t_old(不影响线上) - 注意:该操作会短暂持有元数据锁(MDL),需避开高峰
硬链接 + 异步 rm 这类 OS 层技巧风险极高,除非你完全掌控 buffer pool 清理时机和复制延迟,否则不建议在生产环境尝试。











