delete不缩.ibd文件是innodb设计使然,仅逻辑标记删除并加入可复用链表,页未释放给操作系统,故文件大小不变;需optimize table或alter table engine=innodb重建表才能真正释放空间。

DELETE后磁盘空间没变,不是删错了,也不是配置漏了,就是InnoDB本来就不物理删数据。
为什么DELETE不缩.ibd文件
InnoDB的DELETE只是把行头的delete_mask位设为1,页内空闲空间加入“可复用链表”,但整个页仍保留在.ibd文件里。操作系统看不到“空”,文件大小自然不变。
常见误判现象包括:
-
SHOW TABLE STATUS LIKE 't'\G中Data_length几乎不变,Data_free却明显变大 -
du -sh /var/lib/mysql/db/t.ibd执行前后大小一致 - 监控显示磁盘使用率纹丝不动,哪怕已
DELETE FROM t WHERE created_at
这和MVCC有关:旧版本数据可能被未提交事务引用,purge线程不敢动——所以“删了”不等于“清空物理空间”。
OPTIMIZE TABLE到底做了什么
它本质是重建表:新建临时表 → 拷贝所有未标记删除的行 → 重排聚簇索引 → 删除原.ibd → 原子替换。完成后Data_free ≈ 0,.ibd文件真正缩小。
但要注意几个硬前提:
- 必须确认
innodb_file_per_table = ON(否则OPTIMIZE TABLE对独立.ibd无效) - 磁盘剩余空间 ≥ 当前表
Data_length的1.2倍(临时表+新索引要写两份) - 无长事务阻塞purge:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 600结果应为空 - 表引擎必须是
InnoDB:SHOW CREATE TABLE t\G里得有ENGINE=InnoDB
OPTIMIZE TABLE vs ALTER TABLE ENGINE=InnoDB
在MySQL 5.7+中,OPTIMIZE TABLE t等价于ALTER TABLE t FORCE,两者都会触发重建,但行为细节不同:
-
OPTIMIZE TABLE额外执行ANALYZE TABLE更新统计信息,对后续执行计划更友好 -
ALTER TABLE t ENGINE=InnoDB意图更明确,部分RDS托管服务对其支持更稳定 - 两者都需获取两次MDL锁(开始前+S锁,结束前+X锁),若此时有
SELECT ... FOR UPDATE或慢查询,会卡在Waiting for table metadata lock - 都不支持事务内执行;也不能在从库只读模式下运行(除非显式
SET SESSION sql_log_bin = 0)
什么情况下不该直接OPTIMIZE
别一看到Data_free高就冲上去OPTIMIZE TABLE,容易翻车:
- 表体积 >100GB 或磁盘使用率 >85%:临时空间可能写满,导致
ERROR 1034 (HY000): Incorrect key file for table - 有外键依赖的表:
OPTIMIZE TABLE会失败(报错ERROR 1025),得先SET FOREIGN_KEY_CHECKS = 0再操作 - 主从复制场景:该命令会生成大量binlog事件,可能拖慢从库;建议在低峰期+从库延迟
- 替代方案更合适时:比如能整表清空,优先用
TRUNCATE TABLE t(立刻缩.ibd,但不可回滚、不走binlog row格式)
最常被忽略的一点:OPTIMIZE TABLE不能解决ibdata1膨胀问题。如果innodb_file_per_table = OFF,所有表共用系统表空间,那无论怎么OPTIMIZE,ibdata1都不会变小——只能导出再导入重建整个实例。











