optimize table 通过重建表回收磁盘空间,本质是执行 alter table ... engine=innodb:新建临时表、跳过已删记录、生成紧凑页结构、原子替换;仅对 innodb_file_per_table=on 有效,会锁表且需充足磁盘空间。

DELETE 不会释放磁盘空间,必须显式重建表才能让操作系统回收空间。 这是 InnoDB 的底层机制决定的:删除只是标记记录为“可复用”,页内碎片仍占着 .ibd 文件体积,df -h 看不到变化。真正要缩文件、降磁盘占用,得触发物理重建。
为什么 OPTIMIZE TABLE 能回收空间
它本质是执行一次 ALTER TABLE ... ENGINE=InnoDB 的完整重建流程:新建临时表、逐行拷贝数据(跳过已标记删除的记录)、生成紧凑页结构、原子替换原表。过程中 DATA_FREE 归零,.ibd 文件大小回落到实际数据+索引所需空间。
- 仅对
innodb_file_per_table=ON的表有效 —— 若为 OFF,所有表共享ibdata1,OPTIMIZE TABLE无法缩小它 - 会锁表(DML 阻塞),大表耗时长、IO 压力高,必须避开高峰期
- 重建后
auto_increment值不变,但二级索引顺序重排,可能提升范围查询性能
替代方案:ALTER TABLE ... ENGINE=InnoDB vs FORCE
两者效果等同于 OPTIMIZE TABLE,但行为更可控:
-
ALTER TABLE t ENGINE=InnoDB:MySQL 5.6+ 默认走 online DDL(ALGORITHM=INPLACE),只锁表短暂元数据阶段;若失败会回退,安全边界更高 -
ALTER TABLE t FORCE:强制触发重建,不检查是否真需重建;在某些旧版本或特殊场景下比OPTIMIZE TABLE更稳定 - 二者都不重置
auto_increment,也不清 binlog,适合需要保留自增连续性的场景
执行前必须检查的三项硬条件
盲目执行可能卡死或失败,务必确认:
-
SHOW TABLE STATUS LIKE 't'\G中Engine是InnoDB,且Data_free> 100MB(碎片显著) - 磁盘剩余空间 ≥ 当前
.ibd文件大小 × 2 —— 重建过程要同时存新旧两个文件 -
SELECT @@innodb_file_per_table返回1;否则删表也收不回空间
容易被忽略的副作用和陷阱
很多人只盯着“空间变小”,却踩在这些坑上:
- DDL 过程中
information_schema.TABLES.DATA_FREE会短暂翻倍(因旧表未删完),监控告警可能误报 - 如果表有
FULLTEXT索引,OPTIMIZE TABLE会失败,必须先DROP INDEX再重建 - 使用
mysqldump+DROP+RELOAD虽然彻底,但 binlog 位置丢失、主从延迟突增,生产环境慎用 - MySQL 8.0+ 的
innodb_defragment参数已被废弃,别再查文档配它
真正关键的是:空间回收不是“定期打扫”,而是“按需手术”。先看 DATA_FREE,再算磁盘余量,最后选低峰窗口 —— 缺一不可。否则重建一半磁盘写满,实例就 hang 住了。











