OPTIMIZE TABLE对InnoDB表能否释放磁盘空间取决于innodb_file_per_table是否开启、有无长事务、tmpdir空间是否充足;需查information_schema.TABLES的DATA_FREE字段确认碎片,>1GB或占比>5%才值得干预。

OPTIMIZE TABLE 不是万能解药,对 InnoDB 表是否真能释放磁盘空间,取决于 innodb_file_per_table 是否开启、有没有长事务卡住、tmpdir 是否有足够临时空间——盲目执行常白忙一场。
怎么确认表真有可回收碎片?别信感觉,查 DATA_FREE
删了 80% 数据后 .ibd 文件没变小?先别急着 OPTIMIZE TABLE。直接看 information_schema.TABLES 的 DATA_FREE 字段才是硬指标:
-
DATA_FREE是 InnoDB 内部估算的、当前可复用但尚未归还操作系统的字节数(仅当innodb_file_per_table = 1时有意义) - 单表检查:
SHOW TABLE STATUS LIKE 'your_table';→ 看Data_free列(单位字节) - 批量筛查:
SELECT CONCAT(table_schema,'.',table_name) AS tbl, sys.FORMAT_BYTES(data_free) AS free, (data_free / (data_length + index_length)) AS pct FROM information_schema.tables WHERE engine ='InnoDB' AND data_free > 100*1024*1024 ORDER BY data_free DESC LIMIT 10; -
DATA_FREE为 0 不代表绝对无碎片(比如页内填充率低),但 > 1GB 或占比 > 5% 才值得干预
OPTIMIZE TABLE 为什么经常“没反应”?常见失效场景
执行完发现 .ibd 大小纹丝不动,大概率掉进以下坑里:
-
innodb_file_per_table关闭了:查SELECT @@innodb_file_per_table;,返回 0 就说明所有表共用ibdata1,OPTIMIZE TABLE根本无法把空间还给操作系统 - 表上有长事务未提交:
OPTIMIZE会卡在Waiting for table flush,用SHOW PROCESSLIST;能看到;必须等事务结束或KILL掉 -
tmpdir空间不足:重建过程需额外空间 ≥ 当前.ibd大小;若磁盘只剩 20GB,而表文件 25GB,命令会失败或卡死 - 用了压缩表(
ROW_FORMAT=COMPRESSED):MySQL 5.7 某些小版本对压缩表OPTIMIZE静默跳过,不报错也不生效
比 OPTIMIZE TABLE 更可控的替代操作
生产环境建议优先用 ALTER TABLE your_table ENGINE=InnoDB;,语义清晰、行为可预期:
- 效果等同于
OPTIMIZE TABLE,但不会被误认为“只是优化”,更利于协作理解 -
ALGORITHM=INPLACE对碎片回收无效——InnoDB 碎片整理必须重建表,强制拷贝数据,该参数只影响加索引等少数 DDL - 想降低风险?MySQL 8.0+ 可搭配
LOCK=NONE(需满足条件),或直接用pt-online-schema-change --alter="ENGINE=InnoDB" D=your_db,t=your_table --execute - 超大表(>100GB)务必避开业务高峰,且提前验证
tmpdir剩余空间是否 ≥ 表大小 × 1.2
真正释放空间的关键点容易被忽略
innodb_file_per_table = ON 是前提,但不是全部。即使开了,InnoDB 默认也不会把释放的空间还给操作系统,而是保留在数据文件里供后续插入复用。要让 .ibd 文件真正变小,必须满足:innodb_fast_shutdown = 0(确保 shutdown 时清理缓冲)、tmpdir 有足够空间完成重建、且没有其他连接正在写入该表——这三个条件缺一不可,而最后一条最容易被监控脚本或后台任务绕过。


















