truncate在innodb和myisam下行为不一致:myisam直接重建表并立即释放磁盘空间,innodb仅逻辑清空数据、重置auto_increment,物理空间不立即归还,需optimize table或特定配置才回收。

TRUNCATE在InnoDB和MyISAM下的行为是否一致
基本一致,但底层释放空间的时机和可见性有关键差异。InnoDB下TRUNCATE TABLE会立即重置ibd文件逻辑大小(即SELECT COUNT(*)返回0),但物理磁盘空间通常不立刻归还操作系统;而MyISAM执行后.MYD文件会被直接截断,du -sh能立刻看到体积下降。
常见错误现象:TRUNCATE后df -h显示磁盘使用率没变——这在InnoDB中完全正常,不是失败,也不代表数据没清掉。
- InnoDB:需后续执行
OPTIMIZE TABLE或等待innodb_file_per_table=ON且配合innodb_fast_shutdown=0的干净关闭,才可能真正回收空间 - MyISAM:只要没被其他进程打开,
.MYD文件立即变零字节,ls -lh可验证 - 两者都重置
AUTO_INCREMENT为1,都不触发ON DELETE规则
TRUNCATE失败却没报错?检查FOREIGN_KEY_CHECKS和权限
MySQL 8.0+默认开启外键约束检查,若表被其他表通过FOREIGN KEY引用,TRUNCATE TABLE会直接报错:ERROR 1701 (HY000): Cannot truncate a table referenced in a foreign key constraint,而不是静默失败。
绕过方式必须显式控制约束检查开关:
- 先执行
SET FOREIGN_KEY_CHECKS = 0 - 再执行
TRUNCATE TABLE t1 - 最后务必执行
SET FOREIGN_KEY_CHECKS = 1(漏掉会导致后续插入/更新违反外键逻辑) - 注意:
TRUNCATE本身是隐式提交,执行后无法回滚,上述SET语句也受此影响
权限方面,TRUNCATE需要DROP权限(MySQL 8.0+中已包含在ALTER权限里),仅DELETE权限不够——用普通应用账号执行时容易卡住无响应,查SHOW PROCESSLIST会显示Waiting for table metadata lock。
千万级表上TRUNCATE仍卡住?排查活跃事务和元数据锁
TRUNCATE本质是DDL操作,会申请SCH_M(schema modification)锁,若表上有长事务未提交、或有慢查询正在扫描该表,TRUNCATE就会被阻塞,表现为“一直没返回”,而非报错。
快速定位方法:
- 查阻塞源:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60 - 看谁在锁表:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 't1' - 确认后kill掉对应线程:
KILL <code>trx_mysql_thread_id
别依赖SHOW FULL PROCESSLIST找Updating状态——那是DELETE的典型表现,TRUNCATE卡住时状态通常是Waiting for table metadata lock或Waiting for table flush。
为什么TRUNCATE比DELETE快几十倍?关键在日志和页管理
核心区别不在“删得快”,而在“根本不删”——TRUNCATE不走InnoDB行记录删除流程,它直接丢弃整个段(segment),让InnoDB把对应ibd文件的B+树根页标记为空闲,后续插入新数据时复用空间。
对比项:
-
DELETE FROM t:逐行遍历聚簇索引,每行生成undo log + redo log,标记为deleted,MVCC仍需维护旧版本链,事务提交后才真正清理(purge线程异步做) -
TRUNCATE TABLE t:不生成undo/redo(除元数据变更外),不访问任何用户数据页,不触发purge,不走缓冲池淘汰逻辑 - 性能差距在百万级以上就明显:1000万行表,
DELETE常耗时20–90秒,TRUNCATE通常
真正容易被忽略的点:如果表上有全文索引(FULLTEXT),InnoDB 5.6+仍支持TRUNCATE,但MySQL 5.7之前版本对含全文索引的表TRUNCATE会退化为DELETE行为——务必确认你的MySQL小版本号。











