optimize table能真正释放磁盘空间,因其本质是重建整张表:创建新表→拷贝有效行→重建索引→原子替换→删除旧.ibd文件,使data_free归零、.ibd文件大小回落至实际占用水平。

DELETE 不会释放磁盘空间,这是 InnoDB 的正常行为,不是 bug,也不是你操作错了。 它只是把行标记为“已删除”,物理页仍保留在 .ibd 文件里,等待后续 INSERT 复用。真想缩表、减文件大小,必须显式触发重建——OPTIMIZE TABLE 是最直接的手段。
为什么 OPTIMIZE TABLE 能真正释放空间
它本质是重建整张表:创建新空表 → 拷贝所有未被标记删除的有效行 → 重建索引 → 原子替换原表文件 → 删除旧 .ibd。整个过程绕过了“标记删除”的中间态,让 Data_free 归零、.ibd 文件大小回落到实际数据+索引占用水平。
- 仅对
InnoDB、MyISAM等支持引擎生效;对MEMORY或视图无效 - 在 MySQL 5.7+ 中,
OPTIMIZE TABLE t对 InnoDB 表等价于ALTER TABLE t FORCE,会自动加 ANALYZE,无需额外统计更新 - 不适用于只读实例(如阿里云 RDS 只读节点),会报错
ERROR 1788 (HY000): Statement violates GTID consistency - 执行期间表不可写(S 锁),但允许 SELECT;若并发 DML 频繁,可能引发锁等待堆积
执行前必须检查的三件事
盲目跑 OPTIMIZE TABLE 容易翻车,尤其在线上环境。重点盯住这三项:
- 确认磁盘剩余空间 ≥ 当前表
Data_length的 1.5 倍——临时表和日志要地方落脚,否则中途失败会卡死 - 查活跃长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 600;若有,先协调业务方结束,否则 purge 卡住,OPTIMIZE 也难清理干净 - 看碎片程度:
SHOW TABLE STATUS LIKE 't'\G,若Data_free值远超Data_length的 10%,说明值得优化;若才几百 KB,纯属白忙
OPTIMIZE TABLE 和 ALTER TABLE ENGINE=InnoDB 有啥区别
两者都能重建表、释放空间,但行为细节不同,容易踩坑:
-
OPTIMIZE TABLE t:MySQL 内置命令,语义明确;在 5.7+ 自动走 Online DDL(ALGORITHM=INPLACE),但仍需两次 MDL 锁(开始前 + 替换后),卡慢查询时会阻塞整个库 DDL -
ALTER TABLE t ENGINE=InnoDB:更底层,部分老版本(如 5.5)可能退化为 copy 算法,全程锁表;RDS 等托管服务常将其封装为“在线重建”,但需手动指定ALGORITHM=INPLACE, LOCK=NONE才真正免锁 - 二者都不支持带 WHERE 的条件优化;想删旧数据又缩容,得先
DELETE WHERE ...,再OPTIMIZE,不能合并 - 执行后记得检查
information_schema.TABLES中Data_free是否归零,以及du -sh /var/lib/mysql/db/t.ibd文件大小是否下降
真正麻烦的从来不是“要不要 OPTIMIZE”,而是“能不能安全地 OPTIMIZE”——长事务、磁盘余量、MDL 锁争用、主从延迟,每个点都可能让操作变成线上事故。别只盯着 SQL,先看 SHOW ENGINE INNODB STATUS\G 里的 Purge 线程进度,再动手。










