OPTIMIZE TABLE对InnoDB表常无效,因其仅在innodb_file_per_table=ON、无长事务、磁盘空间充足时才能缩小.ibd文件;判断碎片需查DATA_FREE字段,>100MB或>5%才干预;失效主因包括该参数关闭、长事务阻塞、临时空间不足及压缩表不支持。

OPTIMIZE TABLE 对 InnoDB 表经常“没效果”,不是命令写错了,而是它根本没满足释放空间的条件。真正能回收磁盘空间的,只有在 innodb_file_per_table=ON、无长事务、临时空间充足的前提下,才可能把 .ibd 文件缩回去。
怎么确认表真有碎片,而不是误判?
别靠“删了很多数据”这种感觉判断,DATA_FREE 才是关键指标:
-
SHOW TABLE STATUS LIKE 'your_table'→ 看Data_free列,单位是字节;长期 > 100MB 且持续增长,才值得干预 - 批量查:运行
SELECT CONCAT(table_schema,'.',table_name) AS tbl, sys.FORMAT_BYTES(data_free) AS free FROM information_schema.tables WHERE engine ='InnoDB' AND data_free > 100*1024*1024 ORDER BY data_free DESC LIMIT 10; -
DATA_FREE为 0 不代表没碎片(比如页内碎片),但 > 5% 或 > 1GB 就该动手了 - 注意:
table_rows是采样估算值,avg_row_length × table_rows只能粗略比对,不能当精确依据
为什么 OPTIMIZE TABLE 执行后 .ibd 大小纹丝不动?
大概率踩进这几个坑:
-
innodb_file_per_table是 OFF:查SELECT @@innodb_file_per_table;,返回 0 就关了;此时所有表共用ibdata1,OPTIMIZE TABLE根本无法把空间还给操作系统 - 表上有长事务未提交:
OPTIMIZE卡在Waiting for table flush,用SHOW PROCESSLIST;能看到;必须等事务结束或KILL掉 - 磁盘临时空间不足:重建过程需额外空间 ≥ 当前
.ibd大小;若只剩 20GB,而表文件 25GB,命令会失败或卡死 - 用了压缩表(
ROW_FORMAT=COMPRESSED):MySQL 5.7 某些小版本对压缩表OPTIMIZE静默跳过,不报错也不生效
ALTER TABLE your_table ENGINE=InnoDB 和 OPTIMIZE TABLE 有什么区别?
对 InnoDB 来说,两者本质一样,都是重建表,但行为更可控:
-
ALTER TABLE your_table ENGINE=InnoDB效果等同于OPTIMIZE TABLE,语义更清晰,协作时不容易误解 -
ALGORITHM=INPLACE对碎片回收无效——InnoDB 碎片整理必须重建表,强制拷贝数据,这个参数只影响加索引等 DDL - 想降低风险?MySQL 8.0+ 可搭配
LOCK=NONE(需满足无全文索引、无虚拟列等限制) - 超大表(>50GB)优先用
pt-online-schema-change,通过影子表+触发器实现零锁表整理
执行前必须检查的三个配置项
漏掉任一,OPTIMIZE TABLE 可能白跑一趟,甚至引发主从延迟或 OOM:
-
innodb_file_per_table:必须为 ON,否则空间无法归还文件系统;可通过SELECT @@innodb_file_per_table;确认 -
innodb_fast_shutdown:建议设为 0(即完全关闭),确保 purge 线程完成清理后再重建,避免残留标记删除记录干扰新表 -
tmpdir:确保该路径所在磁盘有足够空闲空间(至少 ≥ 表.ibd大小),否则重建中途失败
Data_free 显著偏高 + 业务查询已感知到性能下降的表,而不是所有表定期轮着跑一遍。


















