delete不缩.ibd文件是因为innodb仅逻辑删除、标记delete_mask并复用页内空间;真正释放需重建表,如optimize table或alter table t engine=innodb。

DELETE 后磁盘空间没变小,不是你删得不对,也不是数据库坏了——InnoDB 就是这么设计的:它只标记行删除,不碰物理文件,更不会通知操作系统“这块空间还给你”。真想让 .ibd 文件变小,必须重建表。
为什么 DELETE 不会缩 .ibd 文件大小
InnoDB 的 DELETE 本质是逻辑操作:
- 把行头的
delete_mask位设为 1,表示“已删除” - 把该行占用的空间加入页内空闲链表(
free list),供后续INSERT复用 - 整块数据页仍保留在
.ibd文件中,文件系统完全无感
所以这些现象全是正常的:
-
du -sh table.ibd输出不变 -
SHOW TABLE STATUS中的Data_length几乎不降 - 哪怕
SELECT COUNT(*) = 0,文件尺寸也纹丝不动 -
Data_free反而可能升高——它只是“内部可复用但未归还 OS”的缓冲区,不是已释放空间
真正能释放空间的操作只有重建表
只有物理重建才能让操作系统回收空间。核心就两个命令,效果一致:
-
OPTIMIZE TABLE t:MySQL 内置命令,语义明确;MySQL 5.7+ 等价于ALTER TABLE t FORCE,自动带ANALYZE -
ALTER TABLE t ENGINE=InnoDB:更底层,脚本中更易控制,RDS 多数支持ALGORITHM=INPLACE
它们都干同一件事:
- 新建空
.ibd文件,按当前索引结构重排聚簇索引 - 逐行扫描原表,只拷贝未被标记删除的有效行
- 拷贝完成后原子性地
DROP原表、RENAME新表 - 旧文件由操作系统真正回收
执行后 Data_free 归零,.ibd 大小回落到实际数据 + 索引占用水平。
执行前必须确认的三件事,缺一不可
盲目运行 OPTIMIZE TABLE 或 ALTER TABLE t ENGINE=InnoDB 极易失败或引发事故:
- 查磁盘余量:
df -h /var/lib/mysql,剩余空间 ≥SHOW TABLE STATUS LIKE 't'\G中的Data_length(建议留 1.5 倍冗余) - 查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 600,有结果必须先处理——否则 purge 卡住,重建也无法清理干净 - 确认基础条件:
SHOW CREATE TABLE t\G输出中含ENGINE=InnoDB,且实例级参数innodb_file_per_table = ON(若关闭,所有表共用ibdata1,重建无效)
什么情况下不该直接重建表
重建不是万能解药,尤其对活跃大表,风险常高于收益:
- 表体积 >100GB 或磁盘使用率 >85%:临时文件极易写满,锁表时间不可控
- 只删部分数据?优先用
mysqldump --where="create_time > '2025-01-01'" db t > t_recent.sql导出再重建导入 - 整表清空且允许停机?用
TRUNCATE TABLE t——它删文件再建新.ibd,立刻释放空间(但不支持WHERE,外键子表上会报ERROR 1701)
最容易被忽略的是托管服务限制:阿里云 RDS、PolarDB 等常禁用 OPTIMIZE TABLE,或自动转到只读副本执行——得先查控制台文档,而不是直接扔进脚本里跑。











