truncate比delete快是因为它不逐行操作,而是直接释放数据段、重置fseg_header并截断.ibd文件;delete则必须启动事务、逐行加锁、写undo日志和binlog,遍历聚簇索引。

TRUNCATE 快不是因为“优化得好”,而是它根本没走 DELETE 那条路——它不删数据,是扔掉整块数据页再建个空表。
TRUNCATE 不扫描、不加锁、不写 undo log
DELETE 即使不带 WHERE,InnoDB 也得启动事务、逐行遍历聚簇索引、给每行加 X 锁、打 delete bit、写 undo log 和 binlog。100 万行 = 百万次锁 + 百万条日志 + 百万次刷盘开销。
TRUNCATE 完全绕过这些:它直接释放表对应的数据段(segment),重置 FSEG_HEADER,把 .ibd 文件截断回初始大小(innodb_file_per_table=ON 时可见)。不读一页数据,不碰一行记录。
- SHOW PROCESSLIST 看不到 “Deleting from table”,状态通常是 “Waiting for table metadata lock”(极短)
- 不会触发
innodb_log_file_size写满、不会拖慢其他 DML - undo 表空间不涨,binlog 只记一条
TRUNCATE TABLE t事件(STATEMENT 格式下)
TRUNCATE 实际是隐式 DROP + CREATE
MySQL InnoDB 的 TRUNCATE TABLE 在内部调用的是 ha_innobase::truncate(),行为接近先 DROP TABLE t 再 CREATE TABLE t LIKE ...,只是复用原表定义元数据(如列名、类型、索引结构)。
这意味着:
- 索引 B+ 树深度重置,高水位线归零,碎片彻底清理
-
AUTO_INCREMENT值被重置为 1(或MIN(1, offset)),而DELETE后插入仍从最大 ID +1 开始 - 表统计信息(
DATA_LENGTH,INDEX_LENGTH)立刻反映真实占用,不用等 purge 线程
为什么不能无脑换用 TRUNCATE?
看似快,但限制硬:它不是 DELETE 的加速版,是另一条技术路径,适用边界非常明确。
- 外键被引用时直接报错:
ERROR 1701 (HY000): Cannot truncate a table referenced in a foreign key constraint,必须先SET FOREIGN_KEY_CHECKS = 0或改用DELETE - 依赖
ON DELETE CASCADE清理子表?TRUNCATE 不触发该行为,子表数据残留 - 有
BEFORE/AFTER DELETE触发器?TRUNCATE 完全跳过,审计日志、缓存更新全失效 - 权限不够:需要
DROP权限,不是DELETE权限 - 主从复制风险:STATEMENT 模式下,TRUNCATE 在从库执行不可逆;ROW 模式虽安全,但部分旧版本 MySQL 不支持
大表清空前必须确认的三件事
跑一条 TRUNCATE 可能只要 0.01 秒,但恢复成本可能是小时级甚至天级。
- 查外键依赖:
SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 't' AND CONSTRAINT_SCHEMA = 'db'; - 确认是否真要重置
AUTO_INCREMENT:如果下游系统用 ID 当序列号或做幂等判断,TRUNCATE 后首条插入可能撞上旧 ID - 检查 binlog 格式:
SELECT @@binlog_format;—— 若为 STATEMENT,且业务要求强一致性,优先选带WHERE 1=1的DELETE+ 分批提交
最常被忽略的一点:TRUNCATE 在显式事务中看似能回滚(InnoDB 特性),但崩溃后未提交事务仍可能丢失;它快,但不是“安全的快”。











