TRUNCATE 和 DELETE 本质不是一个量级的操作
它们看起来都“删数据”,但底层机制完全不同:
TRUNCATE是 DDL(数据定义语言),直接重建空表;
DELETE是 DML(数据操作语言),一行行标记删除。这意味着你不能把它们当同一种操作来选,而要先问自己:要不要事务?要不要触发器?要不要保留自增 ID 连续性?
哪些场景下必须用 DELETE,不能用 TRUNCATE
当你遇到以下任一情况,
TRUNCATE就直接失效,只能用
DELETE:
TRUNCATE不支持
WHERE条件 —— 想删 7 天前的日志?只能写
DELETE FROM log_table WHERE created_at < NOW() - INTERVAL 7 DAY;表被外键约束引用(比如
orders被
order_items引用),
TRUNCATE会报错
Cannot truncate a table referenced in a foreign key constraint,必须用
DELETE配合
SET FOREIGN_KEY_CHECKS = 0(慎用)或先删子表 需要触发器响应(如删用户时同步清理缓存),
TRUNCATE完全不触发任何
BEFORE/AFTER DELETE触发器 要求能回滚(ROLLBACK)——
TRUNCATE执行即提交,无法撤销;
DELETE在事务中可随时回滚
TRUNCATE 快在哪?又快得有多危险
TRUNCATE快,是因为它不走行级日志:不记录每条被删的记录,只记“清空了这张表”这一件事;同时直接释放数据页,立刻归还磁盘空间。但这也带来几个硬限制: 执行
TRUNCATE TABLE user_logs后,
AUTO_INCREMENT值一定重置为 1(哪怕原表最大 ID 是 999999) 没有 binlog 行格式记录(ROW-based binlog 下不可见具体行),主从复制虽能同步命令,但无法用于闪回恢复 需要
DROP权限(不是普通
DELETE权限),很多生产账号默认不给 在 InnoDB 中,
TRUNCATE会隐式提交当前事务,如果你在大事务里顺手写了它,前面所有未提交操作也一起落库了
DELETE 不加 WHERE 的坑,比你想的深
很多人以为
DELETE FROM users和
TRUNCATE TABLE users只差个速度,其实还有隐藏差异: 在 InnoDB 中,
DELETE不释放磁盘空间(只是打删除标记),后续
INSERT会复用这些空间;想真正收缩表?得额外跑
OPTIMIZE TABLE users;
AUTO_INCREMENT值不重置(除非重启 MySQL 或手动
ALTER TABLE ... AUTO_INCREMENT = 1) 如果表很大(千万级),
DELETE可能锁表时间长、占满 undo log、甚至触发 OOM —— 此时宁可用分批
DELETE ... LIMIT 10000,也别硬扛 没加
WHERE的
DELETE仍走完整事务流程,可能阻塞其他写入;而
TRUNCATE虽快,但会拿
SCH_M(架构修改锁),同样会阻塞所有并发 DML
真正决定用哪个的,从来不是“哪个更快”,而是“你愿不愿意放弃事务、触发器、自增连续性、以及出错后那一秒的后悔机会”。线上清空日志表?
TRUNCATE稳准狠。清理过期订单?
DELETE加
WHERE+ 分批 + 监控慢查。误删整张业务表?别纠结命令了,赶紧翻备份和 binlog。
