delete不释放磁盘空间是innodb正常行为,仅标记删除;真正释放需alter table t engine=innodb或optimize table重建表,但须确保磁盘空间充足、无长事务且innodb_file_per_table=on。

DELETE 语句不释放磁盘空间,是 InnoDB 的正常行为,不是 SQL 写错了,也不是数据库坏了——它根本就没打算立刻还空间给操作系统。
DELETE 只是标记删除,不碰物理文件
InnoDB 的 DELETE 实际上只做三件事:把行头的 delete_mask 位设为 1、更新页内空闲链表、把该行加入 purge 队列。整块数据页仍保留在 .ibd 文件里,文件系统层面完全无感知。
-
du -sh table.ibd输出不变,SHOW TABLE STATUS中的Data_length几乎不降 - 即使
SELECT COUNT(*) = 0,文件尺寸也纹丝不动 -
Data_free值反而可能变大——这代表“内部可复用但未归还 OS”的页数,不是已释放空间
真正释放空间必须重建表结构
想让 .ibd 文件物理变小,得绕过标记删除机制,走重建路径:
- MySQL 5.7+ 推荐用
ALTER TABLE t ENGINE=InnoDB(语义清晰,适合脚本) -
OPTIMIZE TABLE t效果相同,但在 5.7+ 等价于ALTER TABLE t FORCE,默认加 S 锁,读写虽不断但性能抖动明显 - 两者本质都是:新建空
.ibd→ 拷贝所有未被标记删除的有效行 → 原子替换 → OS 回收旧文件
执行前不检查这三件事,大概率失败
盲目运行重建命令,轻则锁表数小时,重则写满磁盘、阻塞全库:
- 磁盘剩余空间 ≥ 当前表
Data_length的 1.5 倍(临时表 + 日志要落脚) - 查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 600,有结果必须先KILL - 确认
innodb_file_per_table = ON,否则重建无效(数据还在共享表空间ibdata1里)
RDS/PolarDB 等托管服务常禁用 OPTIMIZE TABLE
很多云厂商会拦截或自动转到只读副本执行,有些甚至直接禁用该命令。不查控制台文档就直接跑,可能返回权限错误或静默跳过。
另外,ALTER TABLE ... ENGINE=InnoDB 在分区表上不会触发全局重建——如果表按时间分区,DROP PARTITION 才是唯一能秒级释放空间的操作,但它只适用于明确可丢弃的整段历史数据。











